天天看點

Golang入門之——sync.Pool

sync.Pool 的使用場景

一句話總結:儲存和複用臨時對象,減少記憶體配置設定,降低 GC 壓力。舉個簡單的例子:

type Student struct {
    Name   string
    Age    int32
    Remark [1024]byte
}
var buf, _ = json.Marshal(Student{Name: "Geektutu", Age: 25})
func unmarsh() {
    stu := &Student{}
    json.Unmarshal(buf, stu)
}      

json 的反序列化在文本解析和網絡通信過程中非常常見,當程式并發度非常高的情況下,短時間内需要建立大量的臨時對象。而這些對象是都是配置設定在堆上的,會給 GC 造成很大壓力,嚴重影響程式的性能。

Go 語言從 1.3 版本開始提供了對象重用的機制,即 sync.Pool。sync.Pool 是可伸縮的,同時也是并發安全的,其大小僅受限于記憶體的大小。sync.Pool 用于存儲那些被配置設定了但是沒有被使用,而未來可能會使用的值。這樣就可以不用再次經過記憶體配置設定,可直接複用已有對象,減輕 GC 的壓力,進而提升系統的性能。sync.Pool 的大小是可伸縮的,高負載時會動态擴容,存放在池中的對象如果不活躍了會被自動清理。

如何使用

sync.Pool 的使用方式非常簡單:

聲明對象池

隻需要實作 New 函數即可。對象池中沒有對象時,将會調用 New 函數建立。

var studentPool = sync.Pool{
    New: func() interface{} { 
        return new(Student) 
    },
}      

Get & Put

Get() 用于從對象池中擷取對象,因為傳回值是 interface{},是以需要類型轉換。

Put() 則是在對象使用完畢後,傳回對象池。

stu := studentPool.Get().(*Student)
json.Unmarshal(buf, stu)
studentPool.Put(stu)      

性能測試

struct 反序列化

func BenchmarkUnmarshal(b *testing.B) {
    for n := 0; n < b.N; n++ {
        stu := &Student{}
        json.Unmarshal(buf, stu)
    }
}
func BenchmarkUnmarshalWithPool(b *testing.B) {
    for n := 0; n < b.N; n++ {
        stu := studentPool.Get().(*Student)
        json.Unmarshal(buf, stu)
        studentPool.Put(stu)
    }
}      

測試結果如下:

$ go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: example/hpg-sync-pool
BenchmarkUnmarshal-8           1993   559768 ns/op   5096 B/op 7 allocs/op
BenchmarkUnmarshalWithPool-8   1976   550223 ns/op    234 B/op 6 allocs/op
PASS
ok      example/hpg-sync-pool   2.334s      

繼續閱讀