Go学习_18_Golang的sync.Pool的对象池

Flutter、Golang、Python、编译原理、算法、Chrome原理学习系列文章抢先看请关注【码农帮派】

【Golang学习系列文章,请扫二维码】

 

上一节中我们通过Buffered channel实现了对象池的功能,但是我们发现在Golang的sync包中有一个Pool,sync.Pool其实是对象缓存机制。

 

sync.Pool的对象缓存是和Processor关联的。Processor在Golang的MPG中介绍过,M是Machine,一个M关联一个内核对象;P是协程处理器,是M运行的上下文环境;G就是协程。

 

对于Processor,它的数据对象如下图:

Go学习_18_Golang的sync.Pool的对象池_第1张图片

每个Processor包含一个私有对象和一个共享池,很多资料上将私有对象成为私有对象池,这是不合适的,因为它只能缓存一个对象,因此更为准确的应该是私有对象。

 

其中私有对象是协程安全的,共享池是协程不安全的。私有对象本身已经实现了锁,因此我们在访问私有对象的时候无需自己加锁,而共享池是协程不安全的,因此我们在使用共享池的时候是需要自己加锁的。

 

sync.Pool对象获取的流程:

  1. 尝试从私有对象获取

  2. 私有对象不存在,尝试从当前Processor的共享池中获取

  3. 如果当前Processor共享池也是空的,那么尝试从其他Processor的共享池中获取

  4. 如果所有子池都是空的,那么使用sync.Pool的New函数创建一个新的对象,并返回。

 

sync.Pool对象返回的流程:

  1. 如果私有对象不存在,则存储到私有对象中

  2. 如果私有对象已存在,则放入到当前Processor的共享池中

 

sync.Pool的使用方法:

pool := &sync.Pool {    New: func() interface{} {        return 0    },}
// 获取对象item := pool.Get().(int)
// 对象放回pool.Put(1)

在创建一个sync.Pool的时候,我们指定了一个New函数,保证在私有对象和每个共享池中获取不到对象的时候,可以进行对象初始化创建。

 

sync.Pool的生命周期

  • GC会清除sync.Pool缓存的数据对象

  • sync.Pool对象缓存的有效期为下一次GC之前

 

正是因为sync.Pool对象的周期会受到系统GC的影响,因此不能够被当作一种稳定的对象池来使用,只能当作对象缓存。

Go学习_18_Golang的sync.Pool的对象池_第2张图片

 

上面的代码中,我们首次从sync.Pool中获取对象,是通过New函数创建的,当我们使用放回函数放回一个缓存对象之后,再次获取,发现获取到的就是我们放回的对象数据。

 

我们在程序中手动出发一次GC:

Go学习_18_Golang的sync.Pool的对象池_第3张图片

 

从上面的结果可以看出,即使我们已经向sync.Pool中放回了对象,但是系统GC会将sync.Pool中的对象清空。

 

需要注意的是,我们首次从sync.Pool中获取对象是通过New函数创建的,我们需要将获取的对象放回到sync.Pool中,否则下次获取的时候,sync.Pool仍然是通过New函数重新创建并返回对象:

Go学习_18_Golang的sync.Pool的对象池_第4张图片

 

上面代码中,只有获取并没有放回,两次获取操作都触发了sync.Pool的New函数重新创建对象。

 

下面的代码中,我们在多协程中访问sync.Pool的对象,因为我们在协程中只获取,没有返回数据,所有后面协程获取的对象都是通过New函数创建的新对象:

Go学习_18_Golang的sync.Pool的对象池_第5张图片

 

sync.Pool总结:

  • sync.Pool适用于通过复用,来降低复杂对象的创建和GC的代价

  • sync.Pool协程安全的,内部实现了锁机制,会有锁的性能开销

  • sync.Pool中对象的生命周期受到GC的影响,不适合用作链接缓存池,也不适用于需要自己管理对象生命周期的对象池

你可能感兴趣的:(Go,Golang,golang)