注意事先:
許多建議使用sync.Pool
這是臨時對象快速,良好的執行。但請注意,sync.Pool
不保證彙集的對象被保留。從它的文檔引用:
存儲在池的任何項目可自動地在任何時候被除去而不通知。如果池在這種情況發生時只有唯一的引用,則該項可能會被釋放。
所以,如果你不想在Pool
你的對象被垃圾收集(這取決於你的情況可能會導致更多的分配),下面提出的解決方案是更好,因爲在通道的緩衝值不垃圾收集。如果你的對象真的很大,那麼內存池是合理的,池通道的開銷將被攤銷。
最簡單的內存池「實現」是一個緩衝通道。
假設你想要一個大對象的內存池。創建一個緩衝通道,指向這些昂貴的對象的值,並且每當你需要時,從池(通道)接收一個。當你使用它時,把它放回池中(在頻道上發送)。爲避免意外丟失物品(例如發生恐慌),請在退回時使用defer
聲明。
讓我們用這個作爲我們的大對象的類型:
type BigObject struct {
Id int
Something string
}
創建池:
pool := make(chan *BigObject, 10)
池的大小僅僅是通道的緩衝區的大小。
灌裝昂貴的對象的指針池(這是可選的,請在最後的筆記):
for i := 0; i < cap(pool); i++ {
bo := &BigObject{Id: i}
pool <- bo
}
很多夠程使用泳池:
wg := sync.WaitGroup{}
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
bo := <-pool
defer func() { pool <- bo }()
fmt.Println("Using", bo.Id)
fmt.Println("Releasing", bo.Id)
}()
}
wg.Wait()
嘗試它的Go Playground 。
請注意,如果所有「池」對象都在使用,則此實現會阻止。如果你不希望這樣,您可以使用select
強制創建新的對象,如果全部都在使用:
var bo *BigObject
select {
case bo = <-pool: // Try to get one from the pool
default: // All in use, create a new, temporary:
bo = &BigObject{Id:-1}
}
在這種情況下,你不需要把它放回池中。或者你可以選擇嘗試與select
把所有放回池中是否有足夠的空間在游泳池,不阻塞,再次:
select {
case pool <- bo: // Try to put back into the pool
default: // Pool is full, will be garbage collected
}
注:
事先灌裝池是可選的。如果您使用select
嘗試從池中獲取/放回值,池可能最初爲空。
您必須確保您不會在請求之間泄漏信息,例如,請確保您不使用已設置並屬於其他請求的共享對象中的字段和值。
stdlib中有['sync.Pool'](https://golang.org/pkg/sync/#Pool)。除此之外,實施「免費清單」是一項並非特定於Go的技術。 – JimB