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方法時:
  一、先從私有的對象private中獲取緩存對象,如獲取失敗從共享的shared中獲取對象;
  二、如仍是失敗將嘗試從victim中獲取。
  三、從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以前。ui

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影響,不適合用於鏈接池等須要本身管理對象生命週期的池化。指針

相關文章
相關標籤/搜索