sync.Pool
参考文献:
- https://blog.youkuaiyun.com/a348752377/article/details/105333118/
- 《GO语言学习笔记》
- https://blog.youkuaiyun.com/yongjian_lian/article/details/42058893
对象池的好处:
- 减少内存分配的开销;
- 减少垃圾回收的开销。
使用
只有两个方法:Get和Put。
pool := &sync.Pool{
New: func() interface{} {
return NewConnection()
},
}
conn := pool.Get().(*Connection)
// do something
pool.Put(conn)
源码分析
为了减少竞争,给每一个P都分配了本地池(poolLocalInternal)。
type Pool struct {
noCopy noCopy
local unsafe.Pointer // [P]poolLocal数组指针
localSize uintptr // size of the local array
New func() interface{}
}
type poolLocal struct {
poolLocalInternal
pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte // 防止false sharing,百度下
}
type poolLocalInternal struct {
// 只能由对应的P访问,有点TLS的意思
private interface{} // Can be used only by the respective P.
shared []interface{} // Can be used by any P.
Mutex // Protects shared.
}
https://blog.csdn.net/yongjian_lian/article/details/42058893
1)固定到某个PoolLocal,尝试从私有对象获取,如果私有对象非空则返回该对象,并把私有对象置空;
2)如果私有对象是空的时候,就去当前子池的共享列表获取(需要加锁);
3)如果当前子池的共享列表也是空的,那么就尝试去其他P的子池的共享列表偷取一个(需要加锁);
4)如果其他子池都是空的,最后就用用户指定的New函数产生一个新的对象返回。
func (p *Pool) Get() interface{} {
// 返回 poolLocal
l := p.pin()
// 优先从 private 选择
x := l.private
l.private = nil
if x != nil {
return x
}
// 加锁,从 share 区域获取
l.Lock()
// 从 shared 尾部提取缓存对象
last := len(l.shared) - 1
if last >= 0 {
x = l.shared[last]
l.shared = l.shared[:last]
}
l.Unlock()
if x != nil {
return x
}
// 如果提取失败,则需要获取新的缓存对象
return p.getSlow()
}
// 因为如果本地池有私有对象的话,是不需要加锁的,因此性能较高
1)固定到某个poolLocal,如果私有对象为空则放到私有对象;
2)否则加入到该P子池的共享列表中(需要加锁)。
func (p *Pool) Put(x interface{}) {
if x == nil {
return
}
// 获取 poolLocal
l := p.pin()
// 优先放入 private
if l.private == nil {
l.private = x
x = nil
}
if x == nil {
return
}
// 放入 share
l.Lock()
l.shared = append(l.shared, x)
l.Unlock()
}