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