golang并发编程-05-同步-05-临时对象池(sync.Pool)
1.golang并发编程-01多进程编程-01概述2.golang并发编程-01多进程编程-02管道3.golang并发编程-01多进程编程-03信号4.golang并发编程-01多进程编程-04socket5.golang并发编程-02多线程编程-01线程概述6.golang并发编程-02多线程编程-02线程的同步7.golang并发编程-03-协程(Goroutine)概述8.golang并发编程-04-通道-01-基本使用/缓冲通道/非缓冲通道9.golang并发编程-04-通道-02-定时器、断续器10.golang并发编程-05-同步-01-锁的使用(sync包Mutex、RWMutex结构体的常用方法)11.golang并发编程-05-同步-02-条件变量(Cond结构体的常用方法)12.golang并发编程-05-同步-03-原子操作(atomic包)13.golang并发编程-05-同步-04-sync包的once、WaitGroup结构体的常用方法
14.golang并发编程-05-同步-05-临时对象池(sync.Pool)
@
目录
1. sync.Pool结构体
1.1 结构体定义
type Pool struct { noCopy noCopy local unsafe.Pointer localSize uintptr victim unsafe.Pointer victimSize uintptr New func() interface{} }
1.2 作用
创建一个对象池,协程调用线程池取出对象。
如果线程池中没有对象了,线程池会创建新的对象。
1.3 创建线程池(初始化实例)
myPool := &sync.Pool{New:func()interface{}}
实际就是初始化一个Pool类型的结构体,给结构体的成员New一个值(该值是一个返回interface{}的函数)
该结构体类型我们可以在上文中看到。
2. sync.Pool结构体的常用方法
2.1 获取实例
func (p *Pool) Get() interface{}
示例
myPool := &sync.Pool{New:func()interface{}{return xxxx }} myPool.Get()
2.2 放回实例
func (p *Pool) Put(x interface{})
示例
myPool := &sync.Pool{New:func()interface{}{return xxxx }} myPool.Put(xxxx)
3. 示例
3.1 守荆州——存入/取出
目的:
该示例仅为展示“放入”、“取出”对象的效果。(并不具有实战意义)
- 荆州守将出战后不回城
说明:
- 创建线程池荆州,默认没有守将
- 先调用,放关羽镇守
- 五次调用:
第一次关羽出战,
之后线程池中没有对象了,只能新创建的对象,于是四次“新兵”出战
func main() { // 建立对象荆州,默认没守将 var jingZhou = &sync.Pool{New:func()interface{}{return "新兵"}} // 定义关羽 name := "关羽" //放关羽守荆州 jingZhou.Put(name) fmt.Printf("【%s】 奉命驻守荆州 ===> \n",name) // 五次出战 for i := 0 ;i<5;i++{ a := jingZhou.Get().(string) fmt.Printf("荆州守将【%s】出战 \n",a) } }
打印结果
【关羽】 奉命驻守荆州 ===> 荆州守将【关羽】出战 荆州守将【新兵】出战 荆州守将【新兵】出战 荆州守将【新兵】出战 荆州守将【新兵】出战
- 关羽每次出战都回城
func main() { var jingZhou = &sync.Pool{New:func()interface{}{return "新兵"}} name := "关羽" jingZhou.Put(name) fmt.Printf("【%s】 奉命驻守荆州 ===> \n",name) for i := 0 ;i<5;i++{ a := jingZhou.Get().(string) fmt.Printf("荆州守将【%s】出战 \n",a) jingZhou.Put(a) fmt.Printf("荆州守将【%s】回城 \n",a) } }
- 打印结果
【关羽】 奉命驻守荆州 ===> 荆州守将【关羽】出战 荆州守将【关羽】回城 荆州守将【关羽】出战 荆州守将【关羽】回城 荆州守将【关羽】出战 荆州守将【关羽】回城 荆州守将【关羽】出战 荆州守将【关羽】回城 荆州守将【关羽】出战 荆州守将【关羽】回城
对象关羽反复被调出、放入,因此始终没有创建新对象。
如果对是否创建新对象有质疑,可以用atomic 添加一个原子计数器(下个例子,在协程中我们会用到)
3.2 丞相叫我来巡城——多协程使用对象池
说明:
- 启动10000个协程分别调兵巡城;
- 创建招兵函数,军营池兵力不足则招兵。
- 统计调兵次数和招兵次数
// 用来统计招兵次数 var recruitNum int32 func recruit() interface{}{ num := atomic.AddInt32(&recruitNum, 1) fmt.Printf("==== 第 %d 次 招兵 ====\n",num) buffer := make([]byte,1024) return &buffer } func main() { // 兵营,兵力不足则招兵 barracks := &sync.Pool{ New: recruit, } // 多协程并发,实现巡逻任务 numWorkers := 10000 var wg sync.WaitGroup wg.Add(numWorkers) for i := 0; i < numWorkers; i++ { tmp := i go func(tmp int) { defer wg.Done() buffer := barracks.Get() //fmt.Printf("第 %d 次 调兵\n",tmp) _ = buffer.(*[]byte) //fmt.Printf("第 %d 次 调兵回营\n",tmp) defer barracks.Put(buffer) }(tmp) } wg.Wait() fmt.Printf("======== [ 统计 ] ========\n调兵次数:%d \n招兵次数:%d \n", numWorkers,recruitNum) }
说明:
为了效果明显,协程调兵中注释掉了两次打印,因为打印需要耗时,占用兵力时间过长,导致招兵次数大大增加。
- 结果打印
==== 第 3 次 招兵 ==== ==== 第 1 次 招兵 ==== ==== 第 5 次 招兵 ==== ==== 第 6 次 招兵 ==== ==== 第 7 次 招兵 ==== ==== 第 8 次 招兵 ==== ==== 第 9 次 招兵 ==== ==== 第 10 次 招兵 ==== ==== 第 11 次 招兵 ==== ==== 第 12 次 招兵 ==== ==== 第 13 次 招兵 ==== ==== 第 14 次 招兵 ==== ==== 第 15 次 招兵 ==== ==== 第 16 次 招兵 ==== ==== 第 17 次 招兵 ==== ==== 第 4 次 招兵 ==== ==== 第 18 次 招兵 ==== ==== 第 19 次 招兵 ==== ==== 第 20 次 招兵 ==== ==== 第 21 次 招兵 ==== ==== 第 22 次 招兵 ==== ==== 第 2 次 招兵 ==== ==== 第 23 次 招兵 ==== ==== 第 24 次 招兵 ==== ==== 第 25 次 招兵 ==== ==== 第 26 次 招兵 ==== ======== [ 统计 ] ======== 调兵次数:10000 招兵次数:26
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了