欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Golang中的sync.Pool对象

程序员文章站 2022-03-26 20:18:00
Golang中存在一个Sync.Pool 对象,从名字上看像是对象池,但他本质上和实际上的对象池有着很大的区别,下面将详细介绍该对象。 Sync.Pool对象可伸缩、并发安全; 数据结构 sync.Pool对外暴露Get、Put、New三个方法,Get返回Pool中的对象,当没有取得到对象时调用Ne ......

  golang中存在一个sync.pool 对象,从名字上看像是对象池,但他本质上和实际上的对象池有着很大的区别,下面将详细介绍该对象。 sync.pool对象可伸缩、并发安全;

数据结构

type pool struct {
	nocopy nocopy   //标识不可复制对象
	local     unsafe.pointer // 固定大小per-p池,实际类型[p] 
        poollocal
	localsize uintptr        // local大小

	victim     unsafe.pointer // 来之上一个生命周期的指针,victim缓存
	victimsize uintptr        //  victim大小
	new func() interface{}
}
type poollocalinternal struct {
	private interface{}  // 私有空间,只能由局部调度器p使用		
	shared  poolchain   // 共享空间,所有调度器p都可以进行相应操作,本地pushhead/pophead、任意p poptail
}

type poollocal struct {
	poollocalinternal    // poollocal 补齐至两个缓存行的倍数
        //每个缓存行具有 64 bytes,即 512 bit
        //处理器一般拥有 32 * 1024 / 64 = 512 条缓存行
        //一个poollocal与一个p绑定,也就是说一个p持有一个poollocal。每个 poollocal 的大小均为缓存行的偶数倍。
	pad [128 - unsafe.sizeof(poollocalinternal{})%128]byte
}

  sync.pool对外暴露get、put、new三个方法,get返回pool中的对象,当没有取得到对象时调用new创建新对象。put将对象放入pool中,这是对这三个方法的简单理解,集合pool的实现下面将详细介绍;

sync.pool对象获取

  对象的获取也就是调用pool对象上面所说的get方法,再执行get方法时:
  1、先从私有的对象private中获取缓存对象,如获取失败从共享的shared中获取对象;
  2、如还是失败将尝试从victim中获取。
  3、从victim中获取对象失败,调用new方法;

sync.pool对象加入

  将对象加入pool中只需要调用put方法即可,在执行put方法时先尝试将对象放入私有的池private中,如private不为空这将对象放入共享的shared池中;

sync.pool对象生命周期

  pool中对象的生命周期是不可控的,将有go垃圾回收器进行管理,gc会清除sync.pool缓存的对象。1.13版本中引进的victim对象可以理解为二次缓存。
  在第一次执行gc时会将对象放入victim中,在此的数据还是可以获取得到,当gc再此执行时victim中旧数据将被新淘汰得数据替换,此时数据彻底删除;
  pool对象的缓存有效期为下下一次gc之前。

p:= sync.pool{new: func() interface{} {
	return "2"
}}
fmt.println(p.get())    //输出2
p.put("123")
p.put("456")
runtime.gc()         //清除123
fmt.println(p.get())   //输出456

  对象生命周期无法掌控此机制的存在,所以sync.pool对象不适合用作对象池,因为无法控制gc也就无法掌握sync.pool中对象的生命周期;
  sync.pool适合通过复用,降低复杂对象的创建和gc代价协程安全,生命周期受gc影响,不适合用于连接池等需要自己管理对象生命周期的池化。