Go編程庫Sync.Pool用法示例詳解
場景
go 如果頻繁地創(chuàng)建、銷毀對象(比如 http 服務(wù)的 json 對象,日志內(nèi)容等),會(huì)對 GC 造成壓力。比如下面的 Log 函數(shù),在高并發(fā)情況下,需要頻繁地創(chuàng)建和銷毀 buffer。
func Log(w io.Writer, key, val string) {
b := new(bytes.Buffer)
// 按一定的格式打印日志,這一段不是重點(diǎn)
b.WriteString(time.Now().UTC().Format(time.RFC3339))
b.WriteByte(' ')
b.WriteString(key)
b.WriteByte('=')
b.WriteString(val)
b.WriteByte('\n')
w.Write(b.Bytes())
}
這時(shí)候可以考慮復(fù)用這些 buffer。我們可以維護(hù)一個(gè) buffer 的對象池,需要的時(shí)候從對象池拿 buffer,用完放回對象池。這時(shí)候就推薦使用 sync.Pool 了。
sync.Pool 維護(hù)著一組對象池,需要時(shí)從對象池拿對象,不需要放回對象池就可以了。它有這些特點(diǎn):
- 忙時(shí)會(huì)自動(dòng)擴(kuò)容對象池,閑時(shí)會(huì)自動(dòng)縮容;
- 線程安全;
- 對象池的對象,會(huì)未經(jīng)通知地自動(dòng)刪除;
- 不能被 copy。
用法
創(chuàng)建
初始化時(shí)指定 New 方法。sync.Pool 會(huì)通過 New 方法創(chuàng)建對象池的對象。一般返回一個(gè)指針。
// 從對象池里取 buffer 時(shí),如果池里沒 buffer了,則調(diào)用 New 創(chuàng)建一個(gè)新的。
var bufPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
GET & PUT
通過 Get 獲取對象池的對象。當(dāng)使用完畢,通過 Put 把對象返回對象池。
b := bufPool.Get().(*bytes.Buffer) // 從對象池拿 buffer 對象 // 操作對象,這個(gè)不重要 b.Reset() b.WriteString(time.Now().UTC().Format(time.RFC3339)) // 操作完放回對象池 bufPool.Put(b)
優(yōu)化 Log 函數(shù)
Log 函數(shù)可以使用 sync.Pool 的優(yōu)化,代碼如下:
var bufPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
func LogWithPool(w io.Writer, key, val string) {
// 從對象池拿 buffer
b := bufPool.Get().(*bytes.Buffer)
b.Reset()
// 按一定的格式打印日志,這一段不是重點(diǎn)
b.WriteString(time.Now().UTC().Format(time.RFC3339))
b.WriteByte(' ')
b.WriteString(key)
b.WriteByte('=')
b.WriteString(val)
b.WriteByte('\n')
w.Write(b.Bytes())
// 放回對象池
bufPool.Put(b)
}
性能測試
我們對兩個(gè)函數(shù)進(jìn)行性能測試
// 不使用 sync.Pool
func BenchmarkLog(b *testing.B) {
writer := os.NewFile(0, os.DevNull)
for n := 0; n < b.N; n++ {
Log(writer, "path", "/search?a=flowers")
}
}
// 使用 sync.Pool 復(fù)用
func BenchmarkLogWithPool(b *testing.B) {
writer := os.NewFile(0, os.DevNull)
for n := 0; n < b.N; n++ {
LogWithPool(writer, "path", "/search?a=flowers")
}
}
結(jié)果:
> go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: example/pool
cpu: Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz
BenchmarkLog-8 1849365 635.0 ns/op 112 B/op 2 allocs/op
BenchmarkLogWithPool-8 1993304 614.4 ns/op 48 B/op 1 allocs/op
PASS
ok example/pool 4.333s
相比之下,使用 Sync.Pool 和不使用的時(shí)候,內(nèi)存的使用比為 48:112,優(yōu)化效果還是挺明顯的。
參考:
[1]. pkg.go.dev/sync#Pool
以上就是Go編程庫Sync.Pool用法示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Go庫Sync.Pool的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
一些關(guān)于Go程序錯(cuò)誤處理的相關(guān)建議
錯(cuò)誤處理在每個(gè)語言中都是一項(xiàng)重要內(nèi)容,眾所周知,通常寫程序時(shí)遇到的分為異常與錯(cuò)誤兩種,Golang中也不例外,這篇文章主要給大家介紹了一些關(guān)于Go程序錯(cuò)誤處理的相關(guān)建議,需要的朋友可以參考下2021-09-09
Golang實(shí)現(xiàn)對map的并發(fā)讀寫的方法示例
這篇文章主要介紹了Golang實(shí)現(xiàn)對map的并發(fā)讀寫的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-03-03

