Go编程库Sync.Pool用法示例详解
作者:小马别过河
场景
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的资料请关注脚本之家其它相关文章!