一文詳解Go語言中對象池的正確打開方式
什么是對象池
對象池是一種設(shè)計模式,它維護(hù)一組已經(jīng)創(chuàng)建好的對象,當(dāng)需要使用對象時,直接從對象池中獲取,使用完畢后再放回對象池,而不是頻繁地創(chuàng)建和銷毀對象。 這樣可以顯著減少 GC 的壓力,提高程序的性能。
為什么不用 sync.Pool
sync.Pool
是 Go 標(biāo)準(zhǔn)庫提供的對象池實現(xiàn),但它有一些限制:
- GC 不確定性:
sync.Pool
中的對象可能會被 GC 回收,導(dǎo)致每次獲取對象都需要重新創(chuàng)建,失去了對象池的意義。 - 適用場景有限:
sync.Pool
更適合于臨時對象的復(fù)用,對于需要長期存在的對象,效果不佳。 - 控制力不足: 無法精確控制對象池的大小和對象的生命周期。
因此,在某些場景下,我們需要自定義對象池,以獲得更高的性能和控制力。
手?jǐn)]對象池:原理與實現(xiàn)
下面,我們就來手?jǐn)]一個簡單的對象池,并分析其原理。
1. 定義對象池結(jié)構(gòu)體
package main import ( "errors" "fmt" "sync" "time" ) type Pool struct { objects chan interface{} // 使用 channel 存儲對象 factory func() interface{} // 創(chuàng)建對象的工廠函數(shù) mu sync.Mutex // 保護(hù)對象池 } var g_index int = 0 func NewPool(size int, factory func() interface{}) *Pool { if size <= 0 { panic("對象池大小必須大于 0") } pool := make(chan interface{}, size) for i := 0; i < size; i++ { pool <- factory() // 預(yù)先創(chuàng)建對象并放入對象池 } return &Pool{ objects: pool, factory: factory, } } func (p *Pool) Get() interface{} { select { case obj := <-p.objects: return obj // 從對象池中獲取對象 default: // 對象池為空,創(chuàng)建新對象 fmt.Println("create new object") p.mu.Lock() defer p.mu.Unlock() return p.factory() } } func (p *Pool) Put(obj interface{}) error { select { case p.objects <- obj: // 對象放回對象池 return nil default: // 對象池已滿,丟棄對象 obj2 := obj.(*MyObject) fmt.Println("pool is full, discard object", obj2.index) obj = nil return errors.New("pool is full") } } func (p *Pool) Len() int { return len(p.objects) } type MyObject struct { Data string index int } func main() { // 創(chuàng)建對象工廠 objectFactory := func() interface{} { g_index += 1 return &MyObject{Data: "Initial Data", index: g_index} } // 創(chuàng)建對象池,大小為 10 pool := NewPool(10, objectFactory) var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func(idx int) { fmt.Println("pool len:", pool.Len()) obj := pool.Get().(*MyObject) defer func() { wg.Done() pool.Put(obj) }() fmt.Println("from :", obj.Data, obj.index, idx) time.Sleep(time.Millisecond * 2) // 模擬一些工作 }(i) } wg.Wait() }
代碼解釋:
ObjectPool
結(jié)構(gòu)體包含一個pool
channel,用于存儲對象。factory
是一個函數(shù),用于創(chuàng)建新的對象。NewObjectPool
函數(shù)用于創(chuàng)建對象池,并預(yù)先創(chuàng)建指定數(shù)量的對象放入對象池。Get
函數(shù)用于從對象池中獲取對象。如果對象池為空,則調(diào)用factory
創(chuàng)建新的對象。Put
函數(shù)用于將對象放回對象池。如果對象池已滿,則丟棄對象。- 定義了一個
MyObject
結(jié)構(gòu)體,作為對象池中存儲的對象類型。 - 創(chuàng)建一個
objectFactory
函數(shù),用于創(chuàng)建MyObject
對象。 - 創(chuàng)建一個大小為 10 的對象池,并傳入
objectFactory
函數(shù)。 - 從對象池中獲取對象,修改對象的數(shù)據(jù),然后將對象放回對象池。
- 再次從對象池中獲取對象,可以看到對象的數(shù)據(jù)已經(jīng)被修改,說明對象被成功復(fù)用。
總結(jié)
通過手?jǐn)]對象池,我們不僅可以更好地理解對象池的原理,還可以根據(jù)實際需求定制對象池,以獲得更高的性能和控制力。 在需要頻繁創(chuàng)建和銷毀對象的場景下,使用對象池可以顯著提高程序的性能,告別 GC 噩夢!
到此這篇關(guān)于一文詳解Go語言中對象池的正確打開方式的文章就介紹到這了,更多相關(guān)Go對象池內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang?range?slice?與range?array?之間的區(qū)別
這篇文章主要介紹了Golang?range?slice?與range?array?之間的區(qū)別,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-07-07詳解golang開發(fā)中http請求redirect的問題
這篇文章主要介紹了詳解golang開發(fā)中http請求redirect的問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10GoLang string與strings.Builder使用對比詳解
這篇文章主要介紹了GoLang string與strings.Builder使用對比,Builder 用于使用 Write 方法有效地構(gòu)建字符串。它最大限度地減少了內(nèi)存復(fù)制。零值可以使用了。不要復(fù)制非零生成器2023-03-03