开发者

Go编程库Sync.Pool用法示例详解

目录
  • 场景
  • 用法
    • 创建
    • GET & PUT
  • 优化 Log 函数
    • 性能测试

      场景

      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)www.devze.com)
      	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.Wrjavascriptiter, 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             &njsbsp;     1849365               635.0 ns/op &nb开发者_Python学习sp;         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的资料请关注我们其它相关文章!

      0

      上一篇:

      下一篇:

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      最新开发

      开发排行榜