天天看點

Golang中的sync.Pool對象

  Golang中存在一個Sync.Pool 對象,從名字上看像是對象池,但他本質上和實際上的對象池有着很大的差別,下面将詳細介紹該對象。 Sync.Pool對象可伸縮、并發安全;

資料結構

type Pool struct {
	noCopy noCopy   //辨別不可複制對象
	local     unsafe.Pointer // 固定大小per-P池,實際類型[P] 
        PoolLocal
	localSize uintptr        // local大小

	victim     unsafe.Pointer // 來之上一個生命周期的指針,victim緩存
	victimSize uintptr        //  victim大小
	New func() interface{}
}
type poolLocalInternal struct {
	private interface{}  // 私有空間,隻能由局部排程器P使用		
	shared  poolChain   // 共享空間,所有排程器P都可以進行相應操作,本地pushHead/popHead、任意P popTail
}

type poolLocal struct {
	poolLocalInternal    // poolLocal 補齊至兩個緩存行的倍數
        //每個緩存行具有 64 bytes,即 512 bit
        //處理器一般擁有 32 * 1024 / 64 = 512 條緩存行
        //一個poolLocal與一個P綁定,也就是說一個P持有一個poolLocal。每個 poolLocal 的大小均為緩存行的偶數倍。
	pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte
}

           

  sync.Pool對外暴露Get、Put、New三個方法,Get傳回Pool中的對象,當沒有取得到對象時調用New建立新對象。Put将對象放入Pool中,這是對這三個方法的簡單了解,集合Pool的實作下面将詳細介紹;

Sync.Pool對象擷取

  對象的擷取也就是調用Pool對象上面所說的Get方法,再執行Get方法時:

  1、先從私有的對象private中擷取緩存對象,如擷取失敗從共享的shared中擷取對象;

  2、如還是失敗将嘗試從victim中擷取。

  3、從victim中擷取對象失敗,調用New方法;

Sync.Pool對象加入

  将對象加入Pool中隻需要調用Put方法即可,在執行Put方法時先嘗試将對象放入私有的池private中,如private不為空這将對象放入共享的shared池中;

sync.Pool對象生命周期

  Pool中對象的生命周期是不可控的,将有Go垃圾回收器進行管理,GC會清除sync.pool緩存的對象。1.13版本中引進的Victim對象可以了解為二次緩存。

  在第一次執行GC時會将對象放入victim中,在此的資料還是可以擷取得到,當GC再此執行時victim中舊資料将被新淘汰得資料替換,此時資料徹底删除;

  Pool對象的緩存有效期為下下一次GC之前。

p:= sync.Pool{New: func() interface{} {
	return "2"
}}
fmt.Println(p.Get())    //輸出2
p.Put("123")
p.Put("456")
runtime.GC()         //清除123
fmt.Println(p.Get())   //輸出456
           

  對象生命周期無法掌控此機制的存在,是以sync.pool對象不适合用作對象池,因為無法控制GC也就無法掌握sync.pool中對象的生命周期;

  sync.Pool适合通過複用,降低複雜對象的建立和GC代價協程安全,生命周期受GC影響,不适合用于連接配接池等需要自己管理對象生命周期的池化。