Go并发:使用sync.Pool来性能优化

简介

在Go提供如何实现对象的缓存池功能?常用一种实现方式是:sync.Pool, 其旨在缓存已分配但未使用的项目以供以后重用,从而减轻垃圾收集器(GC)的压力。

快速使用

sync.Pool的结构也比较简单,常用的方法有Get、Put

type Pool struct {local     unsafe.Pointer // local fixed-size per-P pool, actual type is [P]poolLocallocalSize uintptr        // size of the local arrayvictim     unsafe.Pointer // local from previous cyclevictimSize uintptr        // size of victims array// New optionally specifies a function to generate// a value when Get would otherwise return nil.// It may not be changed concurrently with calls to Get.New func() any
}
func (p *Pool) Get() any  
func (p *Pool) Put(x any) 

接着,通过一个简单的例子,来看看是如何使用的

package mainimport ("fmt""sync"
)type Object struct {ID int// ...
}func main() {// 1.创建一个sync.Pool对象pool := &sync.Pool{New: func() interface{} {fmt.Println("Creating a new object")return &Object{}},}// 2.pool.Get()方法从池中获取一个对象。如果池中有可用的对象,Get()方法将返回其中一个;否则,它将返回一个新创建的对象obj := pool.Get().(*Object)// 3.操作对象obj.ID = 1// 4.调用pool.Put()方法将对象放回池中pool.Put(obj)objBar := pool.Get().(*Object)fmt.Println("Object ID:", objBar.ID)
}

实践应用

在之前的文章中有提到的享元模式设计模式:flyweight(享元)的在棋牌游戏的应用的案例。今天我们使用sync.Pool对该方案进行优化。
观察在棋牌游戏的代码,虽然解决了每次都要New一个对象的问题,但还存在几个优化点:

不能只能缓存特定的棋牌室类型对象;
并发安全问题

原来是通过Factory工厂+Map实现享元模式,截取其中部分代码如下

package design_modeimport "fmt"var chessPieceUnit = map[int]*ChessPiece{1: {Name:  "車",Color: "紅",PositionX: 1,PositionY: 11,},2: {Name:  "馬",Color: "黑",PositionX: 2,PositionY: 2,},// 其他棋子
}func NewChessPieceUnitFactory() *ChessBoard {board := &ChessBoard{Cards: map[int]*ChessPiece{}}for id := range chessPieceUnit {board.Cards[id] = chessPieceUnit[id]}return board
}

1.重构Factory

接着,我们同sync.Pool修改一下Factory的实现:

pool := &sync.Pool{New: func() interface{} {fmt.Println("Creating a new object")return NewChessBoard()},
}game1 := pool.Get().(*ChessBoard)
game2 := pool.Get().(*ChessBoard)
fmt.Println(game1)
fmt.Println(game2)
fmt.Println(game1.Cards[0] == game2.Cards[0]) 

2. 并发安全问题

2.1 修改模型

为了方便观察,给每个房间(棋牌室)增加一个创建时间

type ChessBoard struct {Cards map[int]*ChessPieceTime  time.Time
} 

2.2 并发测试

启动多个goroutine进行测试

func main() {pool := &sync.Pool{New: func() interface{} {fmt.Println("Creating a new object")return NewChessBoard()},}var wg sync.WaitGroupfor i := 0; i < 10; i++ {wg.Add(1)go func(id int) {defer wg.Done()obj := pool.Get().(*ChessBoard)obj.Time = time.Now()pool.Put(obj)fmt.Printf("Object ID: %v\n", obj.Time)}(i)}wg.Wait()
} 

输出如下:

Creating a new object
Creating a new object
Object ID: 2023-10-22 15:41:50.309343 +0800 CST m=+0.003511901
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201

可见,在多个goroutine的并发情况下,是安全,另外可以观察到,sync.Pool没有一直【Creating a new object】去New很多棋牌室。

小结

sync.Pool是Go语言标准库中的一个类型,它提供了对象的缓存池功能。它的主要用途是存储那些可以被复用的临时对象,以便在需要时快速获取,而不是每次都进行新的对象分配。且多个 goroutine 同时使用 Pool 是安全的。
本文简述了sync.Pool的基础使用,以及了如何使用其对实践棋牌室游戏的案例进行优化过程。

参考

官方doc
设计模式:flyweight(享元

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/118901.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在Ubuntu下安装RabbitMQ服务并异地远程访问?

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…

WeakHashMap 源码解析

目录 一. 前言 二. 源码解析 2.1. 类结构 2.2. 成员变量 2.3. 构造方法 2.4. Entry 2.5. 添加元素 2.6. 扩容 2.7. 删除元素 2.8. 获取元素 一. 前言 WeakHashMap&#xff0c;从名字可以看出它是某种 Map。它的特殊之处在于 WeakHashMap 里的entry可能会被GC自动删除…

新手投资如何分配股票仓位?诺奖得主的秘诀是什么?| 附代码【邢不行】

2023年6月22日&#xff0c;诺贝尔经济学奖得主哈里.马克维茨于美国去世&#xff0c;享年95岁。 作为现代金融先驱者&#xff0c;马科维茨不仅是将数学引入金融的第一人&#xff0c;更用数学解释了分散投资的重要性。 更令人惊叹的是&#xff0c;过去十几年中如果按他的理论在中…

docker部署prometheus+grafana服务器监控(一)

docker-compose 部署prometheusgrafana Prometheus Prometheus 是有 SoundCloud 开发的开源监控系统和时序数据库&#xff0c;基于 Go 语言开发。通过基于 HTTP 的 pull 方式采集时序数据&#xff0c;通过服务发现或静态配置去获取要采集的目标服务器&#xff0c;支持多节点工…

18.2 使用NPCAP库抓取数据包

NPCAP 库是一种用于在Windows平台上进行网络数据包捕获和分析的库。它是WinPcap库的一个分支&#xff0c;由Nmap开发团队开发&#xff0c;并在Nmap软件中使用。与WinPcap一样&#xff0c;NPCAP库提供了一些API&#xff0c;使开发人员可以轻松地在其应用程序中捕获和处理网络数据…

clickhouse、Doris、Kylin对比

clickhouse ClickHouse是俄罗斯的Yandex于2016年开源的列式存储数据库&#xff08;DBMS&#xff09;&#xff0c;使用C语言编写&#xff0c;是基于 MPP 架构的分布式 ROLAP &#xff08;Relational OLAP&#xff09;分析引擎主要用于在线分析处理查询&#xff08;OLAP&#xff…

【Redis系列】在Centos7上安装Redis5.0保姆级教程!

哈喽&#xff0c; 大家好&#xff0c;我是小浪。那么最近也是在忙秋招&#xff0c;很长一段时间没有更新文章啦&#xff0c;最近呢也是秋招闲下来&#xff0c;当然秋招结果也不是很理想&#xff0c;嗯……这里就不多说啦&#xff0c;回归正题&#xff0c;从今天开始我们就开始正…

SaveFileDialog.OverwritePrompt

SaveFileDialog.OverwritePrompt 获取或设置一个值&#xff0c;该值指示如果用户指定的文件名已存在&#xff0c;Save As 对话框是否显示警告。 public bool OverwritePrompt { get; set; } OverwritePrompt 控制在将要在改写现在文件时是否提示用户 https://vimsky.com/…

Elasticsearch:使用 Open AI 和 Langchain 的 RAG - Retrieval Augmented Generation (三)

这是继之前文章&#xff1a; Elasticsearch&#xff1a;使用 Open AI 和 Langchain 的 RAG - Retrieval Augmented Generation &#xff08;一&#xff09; Elasticsearch&#xff1a;使用 Open AI 和 Langchain 的 RAG - Retrieval Augmented Generation &#xff08;二&…

Python必学函数:常用内置函数详解和举例分析

map函数 是根据第一个参数定义的函数&#xff0c;依次作用在序列上&#xff0c;返回一个迭代器 s 1,2,3,4,5 # 将字符串转换成整数列表 list(map(int, s.split(,))) # [1,2,3,4,5]# 求两个连表中元素的和&#xff0c;放入新列表中 data1 [1,2,3] data2 [4,5,6] list(map(l…

Fedora Linux 38下安装音频与视频的解码器和播放器

Fedora Linux 38 操作系统安装好后&#xff0c;默认是没有音频与视频的解码器的&#xff0c;音频与视频的播放体验非常差劲。但是第三方的软件源中有解码器和播放器的软件&#xff0c;需要我们自己手动安装。、 连接互联网&#xff0c;打开Shell命令行&#xff1a; 1. sudo d…

配置Sentinel 控制台

1.遇到的问题 服务网关 | RuoYi 最近调试若依的微服务版本需要用到Sentinel这个组件&#xff0c;若依内部继承了这个组件连上即用。 Sentinel是阿里巴巴开源的限流器熔断器&#xff0c;并且带有可视化操作界面。 在日常开发中&#xff0c;限流功能时常被使用&#xff0c;用…

uni-app配置微信开发者工具

一、配置微信开发者工具路径 工具->设置->运行配置->小程序运行配置->微信开发者工具路径 二、微信开发者工具开启服务端口

YB5302是一款工作于2.7V到6.5V的PFM升压型双节锂电池充电控制集成电路

YB5302 锂电输入升压型双节锂电池充电芯片 概述: YB5302是一款工作于2.7V到6.5V的PFM升压型双节锂电池充电控制集成电路。YB5302采用恒流和准恒压模式(Quasi-CVT™)对电池进行充电管理&#xff0c;内部集成有基准电压源&#xff0c;电感电流检测单元&#xff0c;电池电压检测电…

Remote Local File Inclusion (RFI/LFI)-文件包含漏洞

文件包含是一种功能,在各种开发语言中都提供了内置的文件包含函数。在PHP中,例如,可以使用include()和require()函数来引入另一个文件。这个被引入的文件可以当作PHP代码执行,而忽略其后缀本身。 // if( count( $_GET ) ) if( isset( $file ) )include( $file ); else {he…

易点易动固定资产管理系统:高效盘点海量固定资产的得力助手

固定资产是企业重要的财务资源之一&#xff0c;盘点是保证固定资产准确性和完整性的关键环节。然而&#xff0c;对于拥有海量固定资产的企业来说&#xff0c;传统的手工盘点方式效率低下且容易出错。为了解决这一难题&#xff0c;易点易动固定资产管理系统应运而生。本文将深入…

虹科 | 解决方案 | 非道路移动机械诊断方案

虹科Pico汽车示波器为卡车、拖拉机、叉车、船只、联合收割机、挖掘机开发了专用的测试附件和软件测试菜单&#xff0c;比如 24 V 电池、Bosch Denoxtronic、J1939 通信、发动机和液压传动系统以及部件测试等。我们为从事重型车辆和非道路移动机械的维护与诊断的朋友&#xff0c…

【开题报告】基于微信小程序的旅游攻略分享平台的设计与实现

1.研究背景及意义 旅游已经成为现代人生活中重要的组成部分&#xff0c;人们越来越热衷于探索新的目的地和体验不同的文化。然而&#xff0c;对于旅游者来说&#xff0c;获取准确、可靠的旅游攻略信息并不容易。传统的旅游攻略书籍或网站往往无法提供实时、个性化的建议。因此…

Java New对象分配内存流程

一、流程图 二、流程介绍 1、进行逃逸分析&#xff0c;判断是否能够分配到栈上&#xff1a; Y&#xff1a; 如果能分配到栈上&#xff0c;则进行分配。等方法出栈时&#xff0c;对象内存销毁&#xff0c;可有效减少GC的次数。 N&#xff1a;无法分配到栈上&#xff0c;则判断是…

VMware创建Linux虚拟机之(三)Hadoop安装与配置及搭建集群

Hello&#xff0c;world&#xff01; &#x1f412;本篇博客使用到的工具有&#xff1a;VMware16 &#xff0c;Xftp7 若不熟悉操作命令&#xff0c;推荐使用带GUI页面的CentOS7虚拟机 我将使用带GUI页面的虚拟机演示 虚拟机&#xff08;Virtual Machine&#xff09; 指通过…