中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Go臨時對象池pool怎么用

發布時間:2021-09-15 09:51:15 來源:億速云 閱讀:98 作者:小新 欄目:編程語言

這篇文章給大家分享的是有關Go臨時對象池pool怎么用的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

臨時對象池 pool 是啥?

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

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

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

Pool 在多 goroutine 使用環境中是安全的。

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

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

在 fmt 包中有一個使用 Pool 的例子,它維護了一個動態大小的輸出 buffer。

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

Pool 一旦使用,不能被復制。

Pool 結構體的定義為:

type Pool struct {
 noCopy noCopy

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

 // 當池中沒有可能對象時
 // 會調用 New 函數構造構造一個對象
 New func() interface{}
}

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

Get

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

Get 會優先查找本地 private,再查找本地 shared,最后查找其他 P 的 shared,如果以上全部沒有可用元素,最后會調用 New 函數獲取新元素。

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))
  }
 }
 // 未找到可用元素,調用 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))
  // 對應 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 優先把元素放在 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

當世界暫停,垃圾回收將要開始時, poolCleanup 會被調用。該函數內不能分配內存且不能調用任何運行時函數。原因:
防止錯誤的保留整個 Pool

如果 GC 發生時,某個 goroutine 正在訪問 l.shared,整個 Pool 將會保留,下次執行時將會有雙倍內存

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 應用中,后臺在處理用戶的每條請求時都會為當前請求創建一個上下文環境 Context,用于存儲請求信息及相應信息等。Context 滿足長生命周期的特點,且用戶請求也是屬于并發環境,所以對于線程安全的 Pool 非常適合用來維護 Context 的臨時對象池。

Gin 在結構體 Engine 中定義了一個 pool:

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

初始化 engine 時定義了 pool 的 New 函數:

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

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

ServeHttp:

// 從 pool 中獲取,并轉化為 *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 來維護。

printer 與 它的臨時對象池

// pp 用來維護 printer 的狀態
// 它通過 sync.Pool 來重用,避免申請內存
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)
}

感謝各位的閱讀!關于“Go臨時對象池pool怎么用”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

富民县| 伊宁市| 仁寿县| 凤翔县| 离岛区| 陇南市| 乳源| 九台市| 合作市| 吉首市| 宣化县| 大冶市| 宁明县| 历史| 江都市| 丰都县| 香格里拉县| 通河县| 红安县| 炎陵县| 博爱县| 公安县| 攀枝花市| 电白县| 云霄县| 丰县| 石景山区| 资源县| 云和县| 精河县| 西充县| 九江县| 运城市| 永宁县| 曲阜市| 上高县| 吉隆县| 天台县| 田东县| 中西区| 天祝|