Golang sync.pool的用法(附带实例)
Go 语言本身提供了垃圾回收功能,所以通常我们都会随意地创建对象,对象使用完之后也不需要做任何处理,反正垃圾回收功能会帮助我们回收掉这些不用的对象。
但是,如果程序需要频繁地创建大量对象呢?而且这些对象的生命周期都比较短。频繁地创建对象意味着需要频繁地申请内存,生命周期都比较短意味着需要不停地回收大量内存,程序的性能必然会受到影响。
一方面需要频繁地创建大量对象,另一方面这些对象的生命周期都比较短,那能不能复用这些对象呢?也就是说,使用完对象之后,能不能临时地将对象缓存起来,创建对象的时候再从缓存中获取?这样一方面可以降低垃圾回收的成本,另一方面创建对象的性能也有所提升,一举两得。
Go 语言提供了并发对象池 sync.Pool,从对象池获取对象时,如果有空闲对象,直接返回,否则新创建一个对象返回;当然,使用完对象之后需要将对象放回对象池。
对象池的使用方式参考下面的代码:
执行上面的程序后,输出结果如下所示:
当然,对象池不仅仅能存储普通的结构体对象,还可以用来存储连接、协程等复杂对象,以此实现连接复用和协程复用。
但是,如果程序需要频繁地创建大量对象呢?而且这些对象的生命周期都比较短。频繁地创建对象意味着需要频繁地申请内存,生命周期都比较短意味着需要不停地回收大量内存,程序的性能必然会受到影响。
一方面需要频繁地创建大量对象,另一方面这些对象的生命周期都比较短,那能不能复用这些对象呢?也就是说,使用完对象之后,能不能临时地将对象缓存起来,创建对象的时候再从缓存中获取?这样一方面可以降低垃圾回收的成本,另一方面创建对象的性能也有所提升,一举两得。
Go 语言提供了并发对象池 sync.Pool,从对象池获取对象时,如果有空闲对象,直接返回,否则新创建一个对象返回;当然,使用完对象之后需要将对象放回对象池。
对象池的使用方式参考下面的代码:
package main import ( "fmt" "math/rand" "sync" ) func main() { // 匿名函数,用于创建对象 f := func() interface{} { fmt.Println("init new object") return rand.Intn(1000) } // 初始化对象池 pool := sync.Pool{ New: f, } c := pool.Get() // 首次获取对象,新创建 fmt.Println(c) pool.Put(c) // 将对象放回对象池 c1 := pool.Get() // 第二次获取对象,复用 fmt.Println(c1) c2 := pool.Get() // 对象池没有空闲对象,新创建 fmt.Println(c2) }在上面的代码中:
- 变量 f 是一个匿名函数,用于创建对象。同时,我们返回了一个随机数,用于验证该对象是新建的还是复用之前的;
- 方法 pool.Get 用于从对象池中获取对象,如果对象池中有空闲对象,则直接返回;否则,新创建一个对象并返回;
- 方法 pool.Put 用于将对象放回对象池,这个对象将被标记为空闲对象,后续获取时可能直接返回该对象。
执行上面的程序后,输出结果如下所示:
init new object
81
81
init new object
887
- 第一次从对象池获取对象时,新创建了一个对象,对象的值为 81。使用完之后将该对象放回对象池;
- 第二次从对象池获取对象时,因为对象池中有空闲对象,所以复用了该对象;
- 第三次从对象池获取对象时,因为对象池中没有空闲对象,所以新创建了一个对象,对象的值为 887。
当然,对象池不仅仅能存储普通的结构体对象,还可以用来存储连接、协程等复杂对象,以此实现连接复用和协程复用。