婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁 > 知識庫 > 7分鐘讀懂Go的臨時對象池pool以及其應(yīng)用場景

7分鐘讀懂Go的臨時對象池pool以及其應(yīng)用場景

熱門標(biāo)簽:浙江高速公路地圖標(biāo)注 江西轉(zhuǎn)化率高的羿智云外呼系統(tǒng) 南通如皋申請開通400電話 學(xué)海導(dǎo)航地圖標(biāo)注 西部云谷一期地圖標(biāo)注 地圖標(biāo)注的汽車標(biāo) 中國地圖標(biāo)注省會高清 高德地圖標(biāo)注口訣 廣州呼叫中心外呼系統(tǒng)

臨時對象池 pool 是啥?

sync.Pool 給了一大段注釋來說明 pool 是啥,我們看看這段都說了些什么。

臨時對象池是一些可以分別存儲和取出的臨時對象。

池中的對象會在沒有任何通知的情況下被移出(釋放或者重新取出使用)。如果 pool 中持有某個對象的唯一引用,則該對象很可能會被回收。

Pool 在多 goroutine 使用環(huán)境中是安全的。

Pool 是用來緩存已經(jīng)申請了的 目前未使用的 接下來可能會使用的 內(nèi)存,以此緩解 GC 壓力。使用它可以方便高效的構(gòu)建線程安全的 free list(一種用于動態(tài)內(nèi)存申請的數(shù)據(jù)結(jié)構(gòu))。然而,它并不適合所有場景的 free list。

在同一 package 中獨立運行的多個獨立線程之間靜默共享一組臨時元素才是 pool 的合理使用場景。Pool 提供在多個獨立 client 之間共享臨時元素的機制。

在 fmt 包中有一個使用 Pool 的例子,它維護(hù)了一個動態(tài)大小的輸出 buffer。

另外,一些短生命周期的對象不適合使用 pool 來維護(hù),這種情況下使用 pool 不劃算。這是應(yīng)該使用它們自己的 free list(這里可能指的是 go 內(nèi)存模型中用于緩存 32k小對象的 free list) 更高效。

Pool 一旦使用,不能被復(fù)制。

Pool 結(jié)構(gòu)體的定義為:

type Pool struct {
 noCopy noCopy

 local  unsafe.Pointer // 本地P緩存池指針
 localSize uintptr  // 本地P緩存池大小

 // 當(dāng)池中沒有可能對象時
 // 會調(diào)用 New 函數(shù)構(gòu)造構(gòu)造一個對象
 New func() interface{}
}

Pool 中有兩個定義的公共方法,分別是 Put - 向池中添加元素;Get - 從池中獲取元素,如果沒有,則調(diào)用 New 生成元素,如果 New 未設(shè)置,則返回 nil。

Get

Pool 會為每個 P 維護(hù)一個本地池,P 的本地池分為 私有池 private 和共享池 shared。私有池中的元素只能本地 P 使用,共享池中的元素可能會被其他 P 偷走,所以使用私有池 private 時不用加鎖,而使用共享池 shared 時需加鎖。

Get 會優(yōu)先查找本地 private,再查找本地 shared,最后查找其他 P 的 shared,如果以上全部沒有可用元素,最后會調(diào)用 New 函數(shù)獲取新元素。

func (p *Pool) Get() interface{} {
 if race.Enabled {
  race.Disable()
 }
 // 獲取本地 P 的 poolLocal 對象
 l := p.pin() 
 
 // 先獲取 private 池中的對象(只有一個)
 x := l.private
 l.private = nil
 runtime_procUnpin()
 if x == nil {
  // 查找本地 shared 池,
  // 本地 shared 可能會被其他 P 訪問
  // 需要加鎖
  l.Lock()
  last := len(l.shared) - 1
  if last >= 0 {
   x = l.shared[last]
   l.shared = l.shared[:last]
  }
  l.Unlock()
  
  // 查找其他 P 的 shared 池
  if x == nil {
   x = p.getSlow()
  }
 }
 if race.Enabled {
  race.Enable()
  if x != nil {
   race.Acquire(poolRaceAddr(x))
  }
 }
 // 未找到可用元素,調(diào)用 New 生成
 if x == nil  p.New != nil {
  x = p.New()
 }
 return x
}

getSlow,從其他 P 中的 shared 池中獲取可用元素:

func (p *Pool) getSlow() (x interface{}) {
 // See the comment in pin regarding ordering of the loads.
 size := atomic.LoadUintptr(p.localSize) // load-acquire
 local := p.local       // load-consume
 // Try to steal one element from other procs.
 pid := runtime_procPin()
 runtime_procUnpin()
 for i := 0; i  int(size); i++ {
  l := indexLocal(local, (pid+i+1)%int(size))
  // 對應(yīng) pool 需加鎖
  l.Lock()
  last := len(l.shared) - 1
  if last >= 0 {
   x = l.shared[last]
   l.shared = l.shared[:last]
   l.Unlock()
   break
  }
  l.Unlock()
 }
 return x
}

Put

Put 優(yōu)先把元素放在 private 池中;如果 private 不為空,則放在 shared 池中。有趣的是,在入池之前,該元素有 1/4 可能被丟掉。

func (p *Pool) Put(x interface{}) {
 if x == nil {
  return
 }
 if race.Enabled {
  if fastrand()%4 == 0 {
   // 隨機把元素扔掉...
   // Randomly drop x on floor.
   return
  }
  race.ReleaseMerge(poolRaceAddr(x))
  race.Disable()
 }
 l := p.pin()
 if l.private == nil {
  l.private = x
  x = nil
 }
 runtime_procUnpin()
 if x != nil {
  // 共享池訪問,需要加鎖
  l.Lock()
  l.shared = append(l.shared, x)
  l.Unlock()
 }
 if race.Enabled {
  race.Enable()
 }
}

poolCleanup

當(dāng)世界暫停,垃圾回收將要開始時, poolCleanup 會被調(diào)用。該函數(shù)內(nèi)不能分配內(nèi)存且不能調(diào)用任何運行時函數(shù)。原因:
防止錯誤的保留整個 Pool

如果 GC 發(fā)生時,某個 goroutine 正在訪問 l.shared,整個 Pool 將會保留,下次執(zhí)行時將會有雙倍內(nèi)存

func poolCleanup() { 
 for i, p := range allPools {
  allPools[i] = nil
  for i := 0; i  int(p.localSize); i++ {
   l := indexLocal(p.local, i)
   l.private = nil
   for j := range l.shared {
   l.shared[j] = nil
   }
   l.shared = nil
  }
  p.local = nil
  p.localSize = 0
 }
 allPools = []*Pool{}
}

案例1:gin 中的 Context pool

在 web 應(yīng)用中,后臺在處理用戶的每條請求時都會為當(dāng)前請求創(chuàng)建一個上下文環(huán)境 Context,用于存儲請求信息及相應(yīng)信息等。Context 滿足長生命周期的特點,且用戶請求也是屬于并發(fā)環(huán)境,所以對于線程安全的 Pool 非常適合用來維護(hù) Context 的臨時對象池。

Gin 在結(jié)構(gòu)體 Engine 中定義了一個 pool:

type Engine struct {
 // ... 省略了其他字段
 pool    sync.Pool
}

初始化 engine 時定義了 pool 的 New 函數(shù):

engine.pool.New = func() interface{} {
 return engine.allocateContext()
}

// allocateContext
func (engine *Engine) allocateContext() *Context {
 // 構(gòu)造新的上下文對象
 return Context{engine: engine}
}

ServeHttp:

// 從 pool 中獲取,并轉(zhuǎn)化為 *Context
c := engine.pool.Get().(*Context)
c.writermem.reset(w)
c.Request = req
c.reset() // reset

engine.handleHTTPRequest(c)

// 再扔回 pool 中
engine.pool.Put(c)

案例2:fmt 中的 printer pool

printer 也符合長生命周期的特點,同時也會可能會在多 goroutine 中使用,所以也適合使用 pool 來維護(hù)。

printer 與 它的臨時對象池

// pp 用來維護(hù) printer 的狀態(tài)
// 它通過 sync.Pool 來重用,避免申請內(nèi)存
type pp struct {
 //... 字段已省略
}

var ppFree = sync.Pool{
 New: func() interface{} { return new(pp) },
}

獲取與釋放:

func newPrinter() *pp {
 p := ppFree.Get().(*pp)
 p.panicking = false
 p.erroring = false
 p.fmt.init(p.buf)
 return p
}

func (p *pp) free() {
 p.buf = p.buf[:0]
 p.arg = nil
 p.value = reflect.Value{}
 ppFree.Put(p)
}

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

您可能感興趣的文章:
  • Go語言中使用 buffered channel 實現(xiàn)線程安全的 pool
  • Go語言學(xué)習(xí)技巧之如何合理使用Pool

標(biāo)簽:德宏 東營 保定 曲靖 吐魯番 許昌 貴州 常州

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《7分鐘讀懂Go的臨時對象池pool以及其應(yīng)用場景》,本文關(guān)鍵詞  7分鐘,讀懂,的,臨時,對象,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《7分鐘讀懂Go的臨時對象池pool以及其應(yīng)用場景》相關(guān)的同類信息!
  • 本頁收集關(guān)于7分鐘讀懂Go的臨時對象池pool以及其應(yīng)用場景的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    主站蜘蛛池模板: 泸西县| 广灵县| 十堰市| 米易县| 临清市| 清远市| 达日县| 广宁县| 峨眉山市| 澄江县| 青铜峡市| 高邑县| 腾冲县| 华宁县| 肥西县| 车险| 肇州县| 财经| 农安县| 介休市| 永胜县| 铅山县| 玛纳斯县| 姜堰市| 磐安县| 桓台县| 黄山市| 射阳县| 葵青区| 聂拉木县| 平乐县| 庆阳市| 三穗县| 德阳市| 邯郸县| 二连浩特市| 延庆县| 濮阳县| 荥经县| 南投市| 松溪县|