欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2018年11月14日 08:40:18   作者:Y_xx  
這篇文章主要給大家介紹了關(guān)于如何通過7分鐘讀懂Go的臨時對象池pool以及其應(yīng)用場景的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或使用Go具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧

臨時對象池 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 中獨立運(yùn)行的多個獨立線程之間靜默共享一組臨時元素才是 pool 的合理使用場景。Pool 提供在多個獨立 client 之間共享臨時元素的機(jī)制。

在 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 {
   // 隨機(jī)把元素扔掉...
   // 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)用任何運(yùn)行時函數(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í)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Go語言如何通過通信共享內(nèi)存

    Go語言如何通過通信共享內(nèi)存

    這篇文章主要為大家介紹了Go語言如何通過通信共享內(nèi)存實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • Golang中的錯誤處理的示例詳解

    Golang中的錯誤處理的示例詳解

    這篇文章主要為大家詳細(xì)介紹了Golang中的錯誤處理的相關(guān)資料,文章中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Golang有一定幫助,需要的可以參考一下
    2022-12-12
  • Golang?gRPC?HTTP協(xié)議轉(zhuǎn)換示例

    Golang?gRPC?HTTP協(xié)議轉(zhuǎn)換示例

    這篇文章主要為大家介紹了Golang?gRPC?HTTP協(xié)議轉(zhuǎn)換示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 深入理解Go設(shè)計模式之代理模式

    深入理解Go設(shè)計模式之代理模式

    代理模式是一種結(jié)構(gòu)型設(shè)計模式,?其中代理控制著對于原對象的訪問,?并允許在將請求提交給原對象的前后進(jìn)行一些處理,從而增強(qiáng)原對象的邏輯處理,這篇文章主要來學(xué)習(xí)一下代理模式的構(gòu)成和用法,需要的朋友可以參考下
    2023-05-05
  • Go語言結(jié)構(gòu)體Go range的學(xué)習(xí)教程

    Go語言結(jié)構(gòu)體Go range的學(xué)習(xí)教程

    這篇文章主要為大家介紹了Go語言結(jié)構(gòu)體Go range的學(xué)習(xí)教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Golang 統(tǒng)計字符串字?jǐn)?shù)的方法示例

    Golang 統(tǒng)計字符串字?jǐn)?shù)的方法示例

    本篇文章主要介紹了Golang 統(tǒng)計字符串字?jǐn)?shù)的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • 詳解Go語言的內(nèi)存模型及堆的分配管理

    詳解Go語言的內(nèi)存模型及堆的分配管理

    這篇筆記主要介紹Go內(nèi)存分配和Go內(nèi)存管理,會輕微涉及內(nèi)存申請和釋放,以及Go垃圾回收,文中有詳細(xì)的代碼示例以及圖片介紹,需要的朋友可以參考下
    2023-05-05
  • GoFrame框架Scan類型轉(zhuǎn)換實例

    GoFrame框架Scan類型轉(zhuǎn)換實例

    這篇文章主要為大家介紹了GoFrame框架Scan類型轉(zhuǎn)換的實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • go語言標(biāo)準(zhǔn)庫fmt包的一鍵入門

    go語言標(biāo)準(zhǔn)庫fmt包的一鍵入門

    這篇文章主要為大家介紹了go語言標(biāo)準(zhǔn)庫fmt包的一鍵入門使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • Go語言開發(fā)區(qū)塊鏈只需180行代碼(推薦)

    Go語言開發(fā)區(qū)塊鏈只需180行代碼(推薦)

    這篇文章主要介紹了Go語言開發(fā)區(qū)塊鏈只需180行代碼,文章中將不會涉及工作量證明算法(PoW)以及權(quán)益證明算法(PoS)這類的共識算法。需要的朋友可以參考下
    2018-05-05

最新評論