首頁 > 軟體

Go程式設計庫Sync.Pool用法範例詳解

2022-12-17 14:00:45

場景

go 如果頻繁地建立、銷燬物件(比如 http 服務的 json 物件,紀錄檔內容等),會對 GC 造成壓力。比如下面的 Log 函數,在高並行情況下,需要頻繁地建立和銷燬 buffer。

func Log(w io.Writer, key, val string) {
	b := new(bytes.Buffer)
  // 按一定的格式列印紀錄檔,這一段不是重點
	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())
}

這時候可以考慮複用這些 buffer。我們可以維護一個 buffer 的物件池,需要的時候從物件池拿 buffer,用完放回物件池。這時候就推薦使用 sync.Pool 了。

sync.Pool 維護著一組物件池,需要時從物件池拿物件,不需要放回物件池就可以了。它有這些特點:

  • 忙時會自動擴容物件池,閒時會自動縮容;
  • 執行緒安全;
  • 物件池的物件,會未經通知地自動刪除;
  • 不能被 copy。

用法

建立

初始化時指定 New 方法。sync.Pool 會通過 New 方法建立物件池的物件。一般返回一個指標。

// 從物件池裡取 buffer 時,如果池裡沒 buffer了,則呼叫 New 建立一個新的。
var bufPool = sync.Pool{
	New: func() interface{} {
		return new(bytes.Buffer)
	},
}

GET & PUT

通過 Get 獲取物件池的物件。當使用完畢,通過 Put 把物件返回物件池。

  b := bufPool.Get().(*bytes.Buffer)  // 從物件池拿 buffer 物件
  // 操作物件,這個不重要
	b.Reset()
	b.WriteString(time.Now().UTC().Format(time.RFC3339))
  // 操作完放回物件池
	bufPool.Put(b)

優化 Log 函數

Log 函數可以使用 sync.Pool 的優化,程式碼如下:

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()
  // 按一定的格式列印紀錄檔,這一段不是重點
	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)
}

效能測試

我們對兩個函數進行效能測試

// 不使用 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 複用
func BenchmarkLogWithPool(b *testing.B) {
	writer := os.NewFile(0, os.DevNull)
	for n := 0; n < b.N; n++ {
		LogWithPool(writer, "path", "/search?a=flowers")
	}
}

結果:

> 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 和不使用的時候,記憶體的使用比為 48:112,優化效果還是挺明顯的。

參考:

[1]. pkg.go.dev/sync#Pool

以上就是Go程式設計庫Sync.Pool用法範例詳解的詳細內容,更多關於Go庫Sync.Pool的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com