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中獲取數據,很大程度上下降了鎖競爭。