Go臨時(shí)對(duì)象池pool怎么用-創(chuàng)新互聯(lián)

這篇文章給大家分享的是有關(guān)Go臨時(shí)對(duì)象池pool怎么用的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來(lái)看看吧。

創(chuàng)新互聯(lián)公司自2013年起,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元撫松做網(wǎng)站,已為上家服務(wù),為撫松各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:18982081108

臨時(shí)對(duì)象池 pool 是啥?

sync.Pool 給了一大段注釋來(lái)說(shuō)明 pool 是啥,我們看看這段都說(shuō)了些什么。

臨時(shí)對(duì)象池是一些可以分別存儲(chǔ)和取出的臨時(shí)對(duì)象。

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

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

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

在同一 package 中獨(dú)立運(yùn)行的多個(gè)獨(dú)立線程之間靜默共享一組臨時(shí)元素才是 pool 的合理使用場(chǎng)景。Pool 提供在多個(gè)獨(dú)立 client 之間共享臨時(shí)元素的機(jī)制。

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

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

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

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

type Pool struct {
 noCopy noCopy

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

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

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

Get

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

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

func (p *Pool) Get() interface{} {
 if race.Enabled {
  race.Disable()
 }
 // 獲取本地 P 的 poolLocal 對(duì)象
 l := p.pin() 
 
 // 先獲取 private 池中的對(duì)象(只有一個(gè))
 x := l.private
 l.private = nil
 runtime_procUnpin()
 if x == nil {
  // 查找本地 shared 池,
  // 本地 shared 可能會(huì)被其他 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))
  // 對(duì)應(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)世界暫停,垃圾回收將要開始時(shí), poolCleanup 會(huì)被調(diào)用。該函數(shù)內(nèi)不能分配內(nèi)存且不能調(diào)用任何運(yùn)行時(shí)函數(shù)。原因:
防止錯(cuò)誤的保留整個(gè) Pool

如果 GC 發(fā)生時(shí),某個(gè) goroutine 正在訪問 l.shared,整個(gè) Pool 將會(huì)保留,下次執(zhí)行時(shí)將會(huì)有雙倍內(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)用中,后臺(tái)在處理用戶的每條請(qǐng)求時(shí)都會(huì)為當(dāng)前請(qǐng)求創(chuàng)建一個(gè)上下文環(huán)境 Context,用于存儲(chǔ)請(qǐng)求信息及相應(yīng)信息等。Context 滿足長(zhǎng)生命周期的特點(diǎn),且用戶請(qǐng)求也是屬于并發(fā)環(huán)境,所以對(duì)于線程安全的 Pool 非常適合用來(lái)維護(hù) Context 的臨時(shí)對(duì)象池。

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

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

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

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

// allocateContext
func (engine *Engine) allocateContext() *Context {
 // 構(gòu)造新的上下文對(duì)象
 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 也符合長(zhǎng)生命周期的特點(diǎn),同時(shí)也會(huì)可能會(huì)在多 goroutine 中使用,所以也適合使用 pool 來(lái)維護(hù)。

printer 與 它的臨時(shí)對(duì)象池

// pp 用來(lái)維護(hù) printer 的狀態(tài)
// 它通過 sync.Pool 來(lái)重用,避免申請(qǐng)內(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)
}

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

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。

當(dāng)前標(biāo)題:Go臨時(shí)對(duì)象池pool怎么用-創(chuàng)新互聯(lián)
分享地址:http://bm7419.com/article24/hspce.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計(jì)、網(wǎng)站維護(hù)動(dòng)態(tài)網(wǎng)站、域名注冊(cè)、品牌網(wǎng)站制作、網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

微信小程序開發(fā)