go的sync.pool在实际应用中的讲解和性能分析比较-日常实战总结no.4

2022-04-25 08:47:33 浏览数 (1)

关于sync.pool的使用,我这里先给大家说一下结论:

代码语言:javascript复制
在高并发或者大量的数据请求的场景中,我们会遇到很多问题。
垃圾回收就是其中之一(garbage collection),为了减少优化GC,我们一般想到的方法就是能够让对象得以重用。
这就需要一个对象池来存储待回收对象,等待下次重用,从而减少对象产生数量。
我们可以把sync.Pool类型值看作是存放可被重复使用的值的容器。
此类容器是自动伸缩的、高效的,同时也是并发安全的。为了描述方便,我们也会把sync.Pool类型的值称为临时对象池,而把存于其中的值称为对象值。
这个类设计的目的是用来保存和复用临时对象,以减少内存分配,降低CG压力。
以上的条件都是在一个gc周期内。

sync.pool其实主要的功能是在一个gc的周期内复用保存在池子里面的变量。

下面我们看一下它们的使用,使用其实也非常简单,一个put,一个get。

代码语言:javascript复制
package main

import (
  "sync"
  "fmt"
)

func main() {
  //下面这个是一个很简单的例子,这就是最直接最有效的使用例子,只有put和get方式。
  p := new(sync.Pool)
  p.Put("a")
  fmt.Println(p.Get())
}

下面我们来给一下例子来证明用这个性能好一些。

代码语言:javascript复制
package test

import (
  "testing"
  "sync"
)

//注意命名规范 Benchmark 首字母大写的方法名 参数固定
func BenchmarkA(b *testing.B) {
  for i := 0; i < b.N; i   {
    str := "aaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    go a(str, nil)
  }
}

func BenchmarkB(b *testing.B) {
  p := new(sync.Pool)
  t := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
  p.Put(t)
  for i := 0; i < b.N; i   {
    go a(nil, p)
  }
}

func a(s interface{}, p *sync.Pool) interface{} {
  if s == nil {
    return p.Get()
  } else {
    return s
  }
}

上面的代码BenchmarkA 是定义普通的变量,通过并发协程去读。BenchmarkB是定义sync.pool里面写进去数据,通过并发协程去读,我们看看结果。

代码语言:javascript复制
goarch: amd64
BenchmarkA-8     5000000               387 ns/op
BenchmarkB-8     5000000               353 ns/op
PASS
ok   _/F_/WebServer/www/2017/safephp/go/study/middle_high/test       4.830s

压测结果,明显B方法性能好一点,当然我们如果不是高并发的情况下我们可以不适用sync.pool。sync.pool在高并发的情况下,优化代码的情况下是一种很好的思路。

0 人点赞