Golang 并发编程(十):Golang 中临时对象池
- 陈大剩
- 2025-04-18 23:23:26
- 43
临时对象池介绍
sync.Pool
类型可以被称为临时对象池,它的值可以被用来存储临时的对象,与其他同步工具一样,sync.Pool
类型也属于结构体类型,它的值在被真正使用之后,就不应该复制了。临时对象池 中容量是自动伸缩的、高效的、同时也是并发安全的。
pool := sync.Pool{New: func() interface{} {
return make([]int, 0)
}}
fmt.Println(pool.Get().([]int))
再使用临时对象池的时候,可以为它唯一公开字段 New
赋值。该字段类型是 func() interface{}
,即一个函数类型。赋给该字段的函数会被临时对象池用来创建对象值。不过,该函数一般仅在池中无可用对象值的时候才会被调用,我们把这个函数称为 “对象值生成函数”。
pool := sync.Pool{New: func() interface{} {
return make([]int, 0)
}}
pool.Put([]int{1, 2, 3, 4})
fmt.Println(pool.Get().([]int))
fmt.Println(pool.Get().([]int))
上面例子中,sync.Pool
类型中有两个公开的指针方法:Get
和 Put
。前者的功能是从 临时对象池 中获取一个 interface{}
类型的值,而后者的作用则是把一个 interface{}
类型的值放置在池中。
通过 Get
方法获取的值是任意的。如果一个临时对象池的 Put
方法未被调用过,那么它一定会返回 New
的函数值,如果这个结果是池中的,那么在该方法返回它之前,就一定会把它从池中删除。
pool := sync.Pool{New: func() interface{} {
return make([]int, 0)
}}
fmt.Println(pool.Get().([]int))
fmt.Println(pool.Get().([]int))
fmt.Println(pool.Get().([]int))
fmt.Println(pool.Get().([]int))
输出:
[]
[]
[]
[]
上例中,输出类型是 New: func() interface{}
返回的 []int
类型。
垃圾回收
临时对象池的突出特性是对垃圾回收友好。垃圾回收的执行一般会使临时对象池中的对象值全部被移除。也就是说即使我们永远不会显示地从临时对象池取走某个对象值,该对象也不会永远待在临时对象池中,它的生命周期取决于垃圾回收任务的下一次执行。例子如下:
// 禁用 GC,并保证在 main 函数执行结束前回复 GC
defer debug.SetGCPercent(debug.SetGCPercent(-1))
var count int32
pool := sync.Pool{New: func() interface{} {
return atomic.AddInt32(&count, 1)
}}
// New 字段值的作用。
v1 := pool.Get()
fmt.Printf("Value 1: %v\n", v1)
// 临时对象池的存取。
pool.Put(10)
pool.Put(11)
pool.Put(12)
v2 := pool.Get()
fmt.Printf("Value 2: %v\n", v2)
// 垃圾回收对临时对象池的影响。
debug.SetGCPercent(100)
runtime.GC()
runtime.GC()
v3 := pool.Get()
fmt.Printf("Value 3: %v\n", v3)
pool.New = nil
v4 := pool.Get()
fmt.Printf("Value 4: %v\n", v4)
输出结果:
Value 1: 1
Value 2: 10
Value 3: 2
Value 4: <nil>
解释一下上面代码,
- 先将原子加操作赋值给临时对象池
New
字段; - 调用
get
函数,输出程序的第一行,输出就是由于间接调用new
; - 再调用
pool
的put
函数向该池放入 3 个值; - 调用
get
函数,随机从put
函数中获取一个值,输出 10; - 通过
runtime.GC()
清空pool
池子; - 调用
get
函数,间接调用new
输出 2; - 将
pool.New = nil
; - 调用
get
函数,间接调用new
输出nil
;
注意:我们不应该对池中获取的值有任何假设,因为它可能是池中的任何一个值,也可能是对象值生成函数产生的值,更不应该对临时对象池进行复制,否则会造成恐慌。