真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Go源碼閱讀:sync.Pool-創(chuàng)新互聯(lián)

sync.Pool管理一組可以單獨保存和檢索的臨時對象。目的是緩存已分配但未使用的對象,以供以后重用,從而減輕GC的壓力。核心就是PutGetNew

成都創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供西充網(wǎng)站建設、西充做網(wǎng)站、西充網(wǎng)站設計、西充網(wǎng)站制作等企業(yè)網(wǎng)站建設、網(wǎng)頁設計與制作、西充企業(yè)網(wǎng)站模板建站服務,十年西充做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡服務。sync.Pool的結構體

使用sync.Pool需要提供一個New方法,以便在池中沒有緩存的對象時,調(diào)用New創(chuàng)建

type Pool struct {noCopy noCopy // 靜態(tài)檢查機制:內(nèi)置noCopy結構體的對象在第一次使用后不會再發(fā)生復制

	local     unsafe.Pointer // local 固定大小 per-P 池, 實際類型為[P]poolLocal
	localSize uintptr        // local array 的大小

	victim     unsafe.Pointer // 在上一個GC周期local被poolCleanup函數(shù)放置于此,它可能尚未被清理。     后面再講
	victimSize uintptr        // victims array 的大小

	// 在Get方法失敗的情況下,選擇性的創(chuàng)建一個值
	New func() interface{}
}
poolLocal結構體
type poolLocalInternal struct {private interface{} // 只能被各自的P使用
	shared  poolChain   // 可以被任意P使用
}

type poolLocal struct {poolLocalInternal

    // 對齊到機器的緩存行大小,以避免false sharing  [1]
	pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte
}
  • private只保存一個對象,且只能被擁有當前poolLocalP訪問
  • shared是一個鏈表,可以被其他P訪問
單個sync.Pool在運行時的內(nèi)存狀態(tài)image-20221201122101843Get

有了上面這張圖,Get如何從池中獲取對象,也能猜個七七八八了。

  1. 優(yōu)先選擇當前P對應的poolLocal.private。
  2. 若取不到,優(yōu)先從當前Psharehead鏈表的頭部取出一個對象。
  3. 若取不到,則嘗試從其他線程中sharetail中steal一個對象。
  4. 若取不到,則調(diào)用New創(chuàng)建一個對象。
func (p *Pool) Get() interface{} {// 如果啟用的 race 檢查則先停用
	if race.Enabled {race.Disable()
	}
	// 返回pid,和 poolLocal
	l, pid := p.pin()
	// 嘗試從private中獲取數(shù)據(jù)
	x := l.private
	// 獲取之后將private置nil,相當于從poolLocal中移除對象
	l.private = nil
	// 若從private中獲取失敗
	if x == nil {// 為了更好的利用時間局部性,從 shared 頭部讀取對象
		x, _ = l.shared.popHead()
		// 如果讀取不到,則steal獲取新的緩存對象
		if x == nil {	x = p.getSlow(pid)
		}
	}
	runtime_procUnpin()
	// 恢復 race 檢查
	if race.Enabled {race.Enable()
		if x != nil {	race.Acquire(poolRaceAddr(x))
		}
	}
	// 若還是取不出來則調(diào)用New 創(chuàng)建
	if x == nil && p.New != nil {x = p.New()
	}
	return x
}
竊取對象

竊取的策略

  1. 嘗試從其他p的shared竊取。
  2. 若取不到,嘗試從上一輪GC時被放置(惰性回收)的poolLocalprivate取對象。
  3. 若取不到,嘗試從上一輪GC時被放置(惰性回收)的poolLocalshared取對象。
  4. 還是取不到,返回nil,有Get函數(shù)調(diào)用New創(chuàng)建一個對象。
func (p *Pool) getSlow(pid int) interface{} {// 遍歷所有p的 poolLocal 嘗試從shared中竊取一個對象
	size := runtime_LoadAcquintptr(&p.localSize) // load-acquire
	locals := p.local                            // load-consume
	// Try to steal one element from other procs.
	for i := 0; i< int(size); i++ {l := indexLocal(locals, (pid+i+1)%int(size))
		if x, _ := l.shared.popTail(); x != nil {	return x
		}
	}
	
	// 遍歷所有p的victim
	size = atomic.LoadUintptr(&p.victimSize)
	if uintptr(pid) >= size {return nil
	}
	// 遍歷當前p的victim的private
	locals = p.victim
	l := indexLocal(locals, pid)
	if x := l.private; x != nil {l.private = nil
		return x
	}
	// 遍歷其他p的victim的shared
	for i := 0; i< int(size); i++ {l := indexLocal(locals, (pid+i)%int(size))
		if x, _ := l.shared.popTail(); x != nil {	return x
		}
	}
	atomic.StoreUintptr(&p.victimSize, 0)

	return nil
}
Put

將一個(不確定對象狀態(tài))的對象放入到池中,遵循以下策略。

  1. 優(yōu)先放入private
  2. 如果private已經(jīng)有值,則嘗試放入shared
func (p *Pool) Put(x interface{}) {if x == nil {  return
   }
   // 停用 race
   if race.Enabled {  if fastrand()%4 == 0 { // Randomly drop x on floor.
         return
      }
      race.ReleaseMerge(poolRaceAddr(x))
      race.Disable()
   }
   // 獲取 localPool
   l, _ := p.pin()
   // 優(yōu)先放入 private
   if l.private == nil {  l.private = x
      x = nil
   }
   // 如果不能放入 private 則放入 shared
   if x != nil {  l.shared.pushHead(x)
   }
   runtime_procUnpin()

   // 恢復race
   if race.Enabled {  race.Enable()
   }
}
惰性回收

sync.Pool 的垃圾回收發(fā)生在運行時 GC 開始之前。

var poolcleanup func()
// 利用編譯器標志將 sync 包中的清理注冊到運行時
//go:linkname sync_runtime_registerPoolCleanup sync.runtime_registerPoolCleanup
func sync_runtime_registerPoolCleanup(f func()) {poolcleanup = f
}

// 實現(xiàn)緩存清理
func clearpools() {// clear sync.Pools
	if poolcleanup != nil {poolcleanup()
	}
    (...)
}
清理函數(shù)

victim的使用出現(xiàn)在getslow函數(shù)中,當從其他Pshared中無法竊取到對象時,會嘗試從上一次GC周期時放置的緩存中獲取對象。

這就涉及到了惰性回收,當GC觸發(fā)前poolCleanup會將運行時中所有sync.Pool對象中的poolLocal移動到其對應victim字段,victim會保存一個GC周期后被清除。

func poolCleanup() {// 清空上一GC周期的victim緩存
   for _, p := range oldPools {  p.victim = nil
      p.victimSize = 0
   }

   // 將當前運行時中所有Pool(不同的Pool對象)中的local移動到其victim中
   for _, p := range allPools {  p.victim = p.local
      p.victimSize = p.localSize
      p.local = nil
      p.localSize = 0
   }

   // 互換oldPool和allPools,并將allPools置nil
   oldPools, allPools = allPools, nil
}
備注

才疏學淺,若有疑惑之處,很可能是筆者出錯了。還望不吝賜教。

參考資料

[1] false sharing

2 歐老師:Go source study: sync.Pool

你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧


分享標題:Go源碼閱讀:sync.Pool-創(chuàng)新互聯(lián)
URL標題:http://weahome.cn/article/dshsps.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部