Go语言之sync.Pool

sync.Pool in Golang

Posted by alovn on July 7, 2021

sync.Pool对象池是Golang提供的对象重用机制,可以将它看作存放可重用对象值的容器,一般用于存放已经分配但暂时不用的对象,当需要的时候可以直接从pool中取。sync.Pool是可伸缩、并发安全的,它的大小受限于内存大小。

sync.Pool对象获取

sync.Pool获取对象的过程(Get):

  • 尝试从私有对象获取
  • 若私有对象不存在,从当前Processor的共享池获取
  • 若依然为空,则尝试从其它Processor的共享池获取
  • 若以上所有都是空的,最后从用户指定的New函数产生一个新的对象返回

私有对象是协程安全的,而共享池是非协程安全的。

sync.Pool对象放回

sync.Pool放入对象的过程(Put):

  • 如果私有对象不存在,则保存为私有对象
  • 若私有对象存在,则放入当前Processor的共享池中

sync.Pool对象的生命周期

  • GC会清除sync.Pool缓存的对象
  • 对象缓存有效期为下一次GC之前

示例

sync.Pool公开的方法有:New、Get、Put。先简单看一下它的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pool := sync.Pool{
    New: func() interface{} {
        return "test"
    }
}
s := pool.Get()
fmt.Println(s)

pool.Put("helo")

s = pool.Get()
fmt.Println(s)

//Output:
// test
// hello

如果手动调用下GC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pool := sync.Pool{
    New: func() interface{} {
        return "test"
    }
}
s := pool.Get()
fmt.Println(s)

pool.Put("helo")
runtime.GC()

s = pool.Get()
fmt.Println(s)

//Output:
// test
// test

可以看到再次Get的时候已经不是上次Put进去的内容了,因为它可能已经被GC回收了,这时就会通过New函数来自动创建,如果没有注册New函数就会返回nil。

sync.Pool适合存储一些在多个goroutine间共享的临时对象,通过复用对象、减少GC提高性能。当使用共享池中的对象时,为了协程安全,会有锁的开销。

sync.Pool的生命周期受到GC影响,存放在sync.Pool中的值可能在任何时候被GC删除,它可以高负载下可以动态扩容,不活跃的时候对象池会自动收缩。