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

posted on   运维开发玄德公  阅读(18)  评论(0编辑  收藏  举报  

相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示