Go 1.3 的sync包中加入一个新特性:Pool。官方文档能够看这里http://golang.org/pkg/sync/#Poolgolang
这个类设计的目的是用来保存和复用临时对象,以减小内存分配,下降CG压力。算法
type Pool func (p *Pool) Get() interface{} func (p *Pool) Put(x interface{}) New func() interface{}
下面说说Pool的实现:数组
1.定时清理数据结构
文档上说,保存在Pool中的对象会在没有任何通知的状况下被自动移除掉。实际上,这个清理过程是在每次垃圾回收以前作的。垃圾回收是固定两分钟触发一次。并且每次清理会将Pool中的全部对象都清理掉!多线程
2.如何管理数据并发
先看看两个数据结构app
type Pool struct { local unsafe.Pointer // local fixed-size per-P pool, actual type is [P]poolLocal localSize uintptr // size of the local array // New optionally specifies a function to generate // a value when Get would otherwise return nil. // It may not be changed concurrently with calls to Get. New func() interface{} } // Local per-P Pool appendix. type poolLocal struct { private interface{} // Can be used only by the respective P. shared []interface{} // Can be used by any P. Mutex // Protects shared. pad [128]byte // Prevents false sharing. }
Pool是提供给外部使用的对象。其中的local成员的真实类型是一个poolLocal数组,localSize是数组长度。poolLocal是真正保存数据的地方。priveate保存了一个临时对象,shared是保存临时对象的数组。ui
为何Pool中须要这么多poolLocal对象呢?实际上,Pool是给每一个线程分配了一个poolLocal对象。也就是说local数组的长度,就是工做线程的数量(size := runtime.GOMAXPROCS(0))。当多线程在并发读写的时候,一般状况下都是在本身线程的poolLocal中存取数据。当本身线程的poolLocal中没有数据时,才会尝试加锁去其余线程的poolLocal中“偷”数据。.net
func (p *Pool) Get() interface{} { if raceenabled { if p.New != nil { return p.New() } return nil } l := p.pin() // 获取当前线程的poolLocal对象,也就是p.local[pid]。 x := l.private l.private = nil runtime_procUnpin() if x != nil { return x } l.Lock() 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() }
Pool.Get的时候,首先会在local数组中获取当前线程对应的poolLocal对象。若是private中有数据,则取出来直接返回。若是没有则先锁住shared,有数据则直接返回。线程
为何这里要锁住。答案在getSlow中。由于当shared中没有数据的时候,会尝试去其余的poolLocal的shared中偷数据。
Go语言的goroutine虽然能够建立不少,可是真正能物理上并发运行的goroutine数量是有限的,是由runtime.GOMAXPROCS(0)设置的。因此这个Pool高效的设计的地方就在于将数据分散在了各个真正并发的线程中,每一个线程优先从本身的poolLocal中获取数据,很大程度上下降了锁竞争。