陈大剩博客

Golang 并发编程(十):Golang 中临时对象池

  • 陈大剩
  • 2025-04-18 23:23:26
  • 43

 Golang 中 原子操作

临时对象池介绍

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 类型中有两个公开的指针方法:GetPut。前者的功能是从 临时对象池 中获取一个 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>

解释一下上面代码,

  1. 先将原子加操作赋值给临时对象池 New 字段;
  2. 调用 get 函数,输出程序的第一行,输出就是由于间接调用 new
  3. 再调用 poolput 函数向该池放入 3 个值;
  4. 调用 get 函数,随机从 put 函数中获取一个值,输出 10;
  5. 通过 runtime.GC() 清空 pool 池子;
  6. 调用 get 函数,间接调用 new 输出 2;
  7. pool.New = nil
  8. 调用 get 函数,间接调用 new 输出 nil

注意:我们不应该对池中获取的值有任何假设,因为它可能是池中的任何一个值,也可能是对象值生成函数产生的值,更不应该对临时对象池进行复制,否则会造成恐慌。

分享到:
0

说点儿什么吧

头像

表情

本站由陈大剩博客程序搭建 | 湘ICP备2023000975号| Copyright © 2017 - 陈大剩博客 | 本站采用创作共用版权:CC BY-NC 4.0

站长统计| 文章总数[128]| 评论总数[11]| 登录用户[26]| 时间点[132]

logo

登入

社交账号登录