golang之sync.Pool源码解读

本文详细介绍了Go语言中的sync.Pool,包括MPG调度模型,sync.Pool的用途、实现原理、应用场景和最佳实践。sync.Pool旨在减少对象创建和销毁,提高并发场景下的性能,但不适合用于需要自管理生命周期的对象,如连接池。文章还探讨了Pool中对象的生命周期与GC的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MPG模式

1、要了解sync.Pool得先 了解golang的调度模型MPG。
2、MPG就是让 Go程序 智能地 将 goroutine协程中的 任务合理的分配给每个CPU。goroutine协程 是由Go 的运行时(runtime)调度和管理的,所以 MPG 就是 Go语言运行时(runtime)层面的实现。
3、MPG包含三个部分:M(内核线程)、P(管理一组go协程)、G(一个go协程)

M:Machine,一个M直接关联了一个内核线程, 一个groutine最终是要放到M上执行的;
P:processor,存储当前go协程运行的上下文环境(函数指针,堆栈地址及地址边界),会对自己的go协程队列做一些调度(把占用CPU时间较长的go协程暂停、运行后续的goroutine)当自己的队列消费完了就去全局队列里取,如果全局队列里也消费完了会去其他P的队列里抢任务;
G:指的是Goroutine,其实本质上也是一种轻量级的线程,里面除了存放本goroutine信息外 还有与所在P的绑定等信息


参考文档:https://studygolang.com/articles/26904

sync.Pool

1、golang在 高并发场景下应用时,由于内建的GC垃圾回收机制会影响应用的性能,为了减少GC垃圾回收,golang提供了对象重用的机制,也就是sync.Pool对象池
2、sync.Pool 的Pool是一个临时对象池,对Pool的操作就是 对当前 goroutine绑定的P(MPG的P)进行操作
3、 Pool对象 可以被看作是一个存放可重用对象的值的容器。 (该对象是临时的,暂时不用的)
4、其特点:sync.Pool是可伸缩的,并发安全的。 任何Pool中的值可以在任何时候被删除而不通知,在高负载下可以动态的扩容,在不活跃时对象池会收缩

举例应用

func main()  {
   
	var coll = &sync.Pool{
   New: func() interface{
   }{
   
		return "hello, shenzhen"
	}}

	a := "xiasha"
	coll.Put(a)

	b := coll.Get()
	fmt.Println("第一次取出的东西是:",b)

	c := coll.Get()
	fmt.Println("第二次取出的东西是:",c)
}

输出:
第一次取出的东西是: xiasha
第二次取出的东西是: hello, shenzhen

底层实现

  • Pool结构体对象
    该对象内,除了New方法可以供外部调用,在对象池内生成一个默认对象,其他都是私有成员
    [p]是指MPG模型里的p
type Pool struct {
   
	noCopy noCopy		// 实现了Locker接口的空结构体

	local     unsafe.Pointer // 本地固定大小per-P池,实际类型为[P]poolLocal ,有多少个P数组就有多大,也就是每个P维护了一个本地的poolLocal。

	localSize uintptr        // 本地数组的大小

	victim     unsafe.Pointer // 上一周期的局部
	victimSize uintptr        // 已使用的数组大小

	// New 方法在 Get 失败的情况下,选择性的创建一个值,Get就会返回该值, 否则返回nil
	New func() interface{
   }
}

Pool结构体对象的local 字段,因为本质上是一个poollocal类型,所以介绍一下该类型
如下:每个P(对应CPU)都分配一个本地池,当执行Get或者Put操作的时候,会先将goroutine和某个P的子池关联,再对该子池进行操作
poolLocal 包装了poolLocalInternal 结构体对象,新增了pad成员

type poolLocalInternal struct {
   
	private interface{
   } 	// 私有对象,只能被特定的P访问;因为同一时刻一个P只能执行一个goroutine,所以无需加锁
	shared  poolChain   	// 共享列表对象,可以被任何P访问;对共享列表对象进行操作时,因为可能有多个goroutine同时操作,所以需要加锁。
	// 自己可以从队列的头部存然后从头部取,而别的P可以从尾部取。
}

type poolLocal struct {
   
	poolLocalInternal

	// 防止在128 mod(缓存线大小)=0的广泛平台上进行false sharing错误共享。
	// cache使用中常见的一个问题是false sharing。当不同的线程同时读写同一cache line上不同数据时就可能发生false sharing
	// false sharing会导致多核处理器上严重的系统性能下降
	pad [128 - unsafe.Sizeof(poolLocalInternal{
   })%128]byte
}
  • Put方法实现
    新建一个Pool池子实例后,需要 调用Put方法,Put(x)就是把一个临时对象x放入 该池子里,源码如下:
func 
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值