sync.Pool设计思路与原理
基于Go 1.12
使用实例
sync.Pool设计的目的是用来保存和复用临时对象,以减少内存分配,降低CG压力。
Pool对外暴露的主要有三个接口:
func (p *Pool) Get() interface{
}
func (p *Pool) Put(x interface{
})
New func() interface{
}
Get 返回 Pool 中的任意一个对象。如果 Pool 为空,则调用 New 返回一个新创建的对象。
下面是一个实例代码:
package main
import (
"log"
"sync"
)
func main() {
// 建立对象
var pipe = &sync.Pool{
New:func()interface{
}{
return "Hello, BeiJing"}}
// 准备放入的字符串
val := "Hello,World!"
// 放入
pipe.Put(val)
// 取出
first := pipe.Get().(string)
// 再取就没有了,会自动调用NEW
second := pipe.Get().(string)
}
底层数据结构
sync.Pool 是一个临时对象池。一句话来概括,sync.Pool 管理了一组临时对象,当需要时从池中获取,使用完毕后从再放回池中,以供他人使用。
数据结构定义如下:
type Pool struct {
noCopy noCopy
local unsafe.Pointer // local,固定大小per-P池, 实际类型为 [P]poolLocal
localSize uintptr // local array 的大小
// New 方法在 Get 失败的情况下,选择性的创建一个值, 否则返回nil
New func() interface{
}
}
type poolLocal struct {
poolLocalInternal
// 将 poolLocal 补齐至两个缓存行的倍数,防止 false sharing,
// 每个缓存行具有 64 bytes,即 512 bit
// 目前我们的处理器一般拥有 32 * 1024 / 64 = 512 条缓存行
pad [128 - unsafe.Sizeof(poolLocalInternal{
})%128]byte
}
// Local per-P Pool appendix.
type poolLocalInternal struct {
private interface{
} // 只能被局部调度器P使用
shared []interface{
} // 所有P共享
Mutex // 访问共享数据域的锁
}
一个poolLocal与一个P绑定,也就是说一个P持有一个poolLocal。每个 poolLocal 的大小均为缓存行的偶数倍,包含一个 private 私有对象、shared 共享对象 slice 以及一个 Mutex 并发锁。
Put
Put的过程就是将临时对象放进 Pool 里面。源码如下:
func (p *Pool) Put(x interface{
}) {
if x == <