這篇文章給大家分享的是有關(guān)Go臨時(shí)對(duì)象池pool怎么用的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。
創(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 給了一大段注釋來說明 pool 是啥,我們看看這段都說了些什么。
臨時(shí)對(duì)象池是一些可以分別存儲(chǔ)和取出的臨時(shí)對(duì)象。
池中的對(duì)象會(huì)在沒有任何通知的情況下被移出(釋放或者重新取出使用)。如果 pool 中持有某個(gè)對(duì)象的唯一引用,則該對(duì)象很可能會(huì)被回收。
Pool 在多 goroutine 使用環(huán)境中是安全的。
Pool 是用來緩存已經(jīng)申請(qǐng)了的 目前未使用的 接下來可能會(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 來維護(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 非常適合用來維護(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 來維護(hù)。
printer 與 它的臨時(shí)對(duì)象池
// pp 用來維護(hù) printer 的狀態(tài) // 它通過 sync.Pool 來重用,避免申請(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元起步,三天無理由+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://www.rwnh.cn/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í)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容