扫码订阅《 》或入驻星球,即可阅读文章!

GOLANG ROADMAP

阅读模式

  • 沉浸
  • 自动
  • 日常
首页
Go友会
  • 城市
  • 校园
Go学院
  • Go小课
  • Go小考
  • Go实战
  • 精品课
Go求职
  • 求职辅导🔥
  • Offer收割社群
  • 企业题库
  • 面试宝典
Go宝典
  • 在线宝典
  • B站精选
  • 推荐图书
  • 每日博文
Go仓库
实验区
  • Go周边
  • Go下载
  • Go月刊
消息
更多
  • 用户中心

    • 我的信息
    • 推广返利
  • 玩转星球

    • 星球介绍
    • 角色体系
    • 星主权益
  • 支持与服务

    • 联系星主
    • 成长记录
    • 常见问题
    • 吐槽专区
  • 合作交流

    • 渠道合作
    • 课程入驻
    • 友情链接
author-avatar

GOLANG ROADMAP


首页
Go友会
  • 城市
  • 校园
Go学院
  • Go小课
  • Go小考
  • Go实战
  • 精品课
Go求职
  • 求职辅导🔥
  • Offer收割社群
  • 企业题库
  • 面试宝典
Go宝典
  • 在线宝典
  • B站精选
  • 推荐图书
  • 每日博文
Go仓库
实验区
  • Go周边
  • Go下载
  • Go月刊
消息
更多
  • 用户中心

    • 我的信息
    • 推广返利
  • 玩转星球

    • 星球介绍
    • 角色体系
    • 星主权益
  • 支持与服务

    • 联系星主
    • 成长记录
    • 常见问题
    • 吐槽专区
  • 合作交流

    • 渠道合作
    • 课程入驻
    • 友情链接
  • Go真实面试题汇总系列

    • 《Go基础篇》
  • 宝典内容

    • 3.Go语言中是如何实现继承的?
    • 4.数组怎么转集合?
    • 13.map取一个key,然后修改这个值,原map数据的值会不会变化
    • 16. go struct 能不能比较
    • 19. 数组是如何实现用下标访问任意元素的
    • 25. 深拷贝和浅拷贝
    • 29. go 的优势
    • 30. 如何判断channel是否关闭?
    • 31. make 与 new 的区别
    • 32. Slice 与 Array, Append()
    • 36. go语言的引用类型有什么?
    • 44. 获取不到锁会一直等待吗?
    • 45. 如何实现一个 timeout 的锁?
    • 55. make可以初始化哪些结构
    • 59. 空结构体占不占内存空间? 为什么使用空结构体?
    • 61. defer 是怎么用的
    • 62. Context 包的作用
    • 65. go 语言的 panic 如何恢复
    • 66. defer 的执行顺序
    • 71. go的init函数是什么时候执行的?
    • 72. 多个init函数执行顺序能保证吗?
    • 73. gin框架的路由是怎么处理的?
    • 74. 用火焰图的优势?
    • 75. struct的传递场景
    • 77. go的profile工具
    • 78. 怎么检查go问题
    • 79. context包内部如何实现的?
    • 80. syncpool的实现原理
    • 81. go什么场景使用接口
    • 83. go怎么实现封装继承多态
    • 84. 为什么go的变量申请类型是为了什么?
    • 85. Go和JAVA垃圾回收机制有啥区别
    • 88. node.js和go是基于什么样的考虑是用这两种语言的?
    • 89. golang垃圾回收机制了解吗?
    • 100. 怎么确定走go语言技术栈的
    • 102. 介绍Gin框架
    • 108. 什么时候会触发线程切换
    • 110. defer关键字后的函数在什么时候调用 主函数return前还是return后
    • 113. golang http库设计原理,为什么不池化
    • 114. golang gc
    • 116. 关闭一个已经关闭的 Channel 会发生什么?Channel 有缓存和没缓存的区别是什么?
    • 122. 类型断言用过吗,说说实现,如何判断断言成功?
    • 123. for true {time.Sleep(1)} 有什么问题
    • 124. sleep底层实现原理
    • 126. interface 的底层实现
    • 127. STW 在 go 的哪些阶段发生?了解1.8版本的改进吗?
    • 130. go test test 和 benchmark
    • 144. for range坑输出
    • 145. go结构体和结构体指针的区别
    • 147. go如何避免panic
    • 148. 结构体创建优化
    • 152. golang interface底层实现,使用场景
    • 153. golang类型断言,怎么用
    • 154. 听说go有什么什么的缺陷,你怎么看
    • 155. 对go有哪些认识
    • 156. go和java的区别
    • 158. 对go的中间件和工作机制有了解吗?
    • 175. Go string底层实现?
    • 177. 了解HTTP协议吗?golang HTTP库实现?
    • 186. 使用range输出一个数组,需要注意的问题
    • 187. Go管理依赖go mod命令,go mod最后的版本号如果没有tag,是怎么生成的
    • 188. 进程、线程、协程的区别?
    • 195. 说一说go的defer和chan
    • 196. golang多态、父类方法重写
    • 198. 线程和协程的区别
    • 201. Golang 的结构体的组合(实现java继承的特性)
    • 202. Golang interface的设计
    • 204. context包的用途?
    • 208. Go的数据结构的零值是什么?
    • 218. 进程线程协程的区别
    • 219. go协程的好处
    • 220. byte和rune有什么区别
    • 228. 进程线程协程区别
    • 236. go中的struct 能不能比较
    • 237. go defer
    • 238. select可以用于什么
    • 242. 用go构造一个链表怎么做,想要从链表尾部插入,怎么做
    • 245. Go语言有缓冲Channel与无缓冲Channel区别
    • 253. go channel close后读的问题
    • 256. defer的执行顺序
    • 259. go 怎么实现func的自定义参数
    • 261. defer的执行顺序
    • 262. golang的调试
    • 263. defer recover panic 执行顺序
    • 267. copy是操作符还是内置函数
    • 276. Go有哪些数据结构
    • 293. defer关键字使用
    • 294. channel有缓冲、无缓冲区别
    • 295. defer和recover的配合
    • 304. 写一个东西:一个字符串json,转成一个直接可用的map,字符串可能是任何形式
    • 305. go的通信实现
    • 306. go interface的底层实现
    • 309. go func与method之前的那个receiver的作用
    • 311. 一个函数传参一个 slice,先 append 再赋值和另一个函数先赋值再append,哪个会发生变化?
    • 312. 有没有什么线程安全的办法?
    • 321. go map时间复杂度
    • 322. go 从源码到二进制代码的整个流程
    • 324. select、epoll
    • 329. make原理
    • 330. string类型转为[]byte过程发生了什么
    • 340. runtime
    • 348. go语言中结构体指针为空,赋给一个interface{}为什么interface不为空
    • 349. defer问题
    • 350. 你能介绍一下go的包管理工具吗?除了gomod还知道哪些?
    • 355. go的值传递和引用传递
    • 357. Go的闭包语法
    • 371. go的反射
    • 374. 判断下面代码的输出
    • 377. 对象是什么,面向对象有什么好处,go 中如何实现多态
    • 379. go 的执行顺序
    • 380. golang的基础问题,比如包管理,比如值传递,比如协程
    • 383. 问了golang的interface的区别,继承,gc的原理、区别,双向链表等。
    • 408. go range 的陷阱
    • 411. 考察defer和panic执行顺序的问题
    • 414. Python和Go的区别
    • 416. go的oop与传统的oop的区别
    • 417. go里面interface是什么概念
    • 418. 相比于java、c++,go的interface有什么区别吗?
    • 421. go和node的区别
    • 422. 程序计数器作用,为什么是私有的
    • 423. PHP和 Go 对比
    • 424. defer如何实现
    • 429. 讲讲go的启动过程
    • 430. Go mod主要解决了什么问题
    • 431. Go sum里面是什么内容
    • 437. Go结构体内嵌后的命名冲突
    • 440. Go 的面向对象特性
    • 442. go init 的执行顺序,注意是不按导入规则的(这里是编译时按文件名的顺序执行的)
    • 443. interface和nil 比较。
    • 454. 一个a+b程序从编译到运行都发生了什么(从预编译到print显示到屏幕上)
    • 455. Go中struct组合与Java继承的区别
    • 458. 使用过哪些 golang 的 String 类库
    • 459. golang 断言

扫码订阅《 》或入驻星球,即可阅读文章!

80. syncpool的实现原理


企业题库解析小组

**题目序号:**361

题目来源:

频次: 1

答案1:(趁醉独饮痛)

Pool是什么:

Go标准库中提供的一个通用的Pool数据结构,可以使用它创建池化的对象。sync.Pool数据类型的对象用来保存一组可独立访问的临时对象,注意它是临时的,也就是说sync.Pool中保存的对象会在将来的某个时间从sync.Pool中移除掉,如果也没有被其他对象引用的话,该对象会被垃圾回收掉。

Go是一个自带GC的语言,使用起来没有心智负担,我们想使用一个对象就创建一个对象,想使用多个就创建多个,不用关心对象的释放问题。因为有GC程序会帮助我们自动将不再使用的对象的内存回收掉。但是在使用Go开发高性能的应用程序的时候,需要考虑GC执行垃圾回收给我们带来的影响。在GC执行垃圾回收的过程中需要STW(stop-the-world)时间,如果创建有大量的对象,GC程序在检查这些对象是不是垃圾的时候需要花费很多时间。进而对业务功能造成性能影响。

优点:

  • 减轻GC负担
  • 减少新对象的申请 复用对象,避免每次使用对象都要创建对象
  1. sync.Pool 本身就是线程安全的,多个 goroutine 可以并发地调用它的方法存取对象;
  2. sync.Pool 不可在使用之后再复制使用(struct 中有 Nocopy 静态检查是否复制使用)

三个方法:

**New:**当调用 Pool 的 Get方法从池中获取元素,

        1. 没有更多的空闲元素可返回时,就会调用这个 New 方法来创建新的元素。
        2. 没有设置 New 字段,没有更多的空闲元素可返回时,Get 方法将返回 nil,表明当前没有可用的元素。
        3. **Get:从Pool 移除一个元素,这个元素会从 Pool 中移除,返回给调用者,返回值可能是 nil**

Put:将一个元素返还给 Pool,Pool 会把这个元素保存到池中,并且可以复用。但如果 Put 一个 nil 值,Pool 就会忽略这个值

原理:

(sync.Pool结构图)

1.13< 版本之前的两个问题:

  • 每次 GC 都会回收创建的对象。 1. 元素过多会导致 SWT 耗时变长 2. 缓存元素都被回收后,会导致Get 命中率下降,不得不创建心的对象
  • 底层实现使用了 Mutex,对这个锁并发请求竞争激烈的时候,会导致性能的下降。 3. Go 对 Pool 的优化就是避免使用锁,同时将加锁的 queue 改成 lock-free 的 queue 的实现,给即将移除的元素再多一次“复活”的机会。

Pool 最重要的两个字段 (local 和 victim)

  1. local:

local是一个poolLocal数组的指针,localSize代表这个数组的大小,它的大小是goroutine中P的数量。每个P都有一个ID,这个ID的值对应着poolLoca数组的下标索引。所以poolLocal数组大小最大为runtime.GOMAXPROCS(0)。victim对应local,它也是一个poolLocal数组的指针,victimSize是victim数组的大小。每次垃圾回收的时候,Pool会把victim中的对象清理掉,然后把local的数据赋值给victim,并把local设置为nil,victim像一个中转站,里面的数据可以被捡回来重新使用,也可能被GC回收掉。

  1. Victim:

victim 中的元素如果被 Get 取走,那么这个元素就很幸运,因为它又“活”过来了。但是,如果这个时候 Get 的并发不是很大,元素没有被 Get 取走,那么就会被移除掉,因为没有别人引用它的话,就会被垃圾回收掉。

type Pool struct {
 noCopy noCopy
 // local是一个指向[p]poolLocal的指针,它指向的是一个数组,此数组中的元素是poolLocal类型
 // 数组的大小是固定的,数量与p的数量是相同的,也是每个p对应数组中的1个元素
 local unsafe.Pointer 
 // local数组的大小
 localSize uintptr 
 // victim类型与local是一样的,可以理解为local的中转站,过渡存储local用的
 victim unsafe.Pointer 
 // victim数组的大小
 victimSize uintptr 
 // New方法,返回的空接口类型,该字段也是Pool唯一暴露给外界的字段
 // New方法可以赋值一个能够产生值的函数,在调用Get方法的时候可以用
 // New方法来产生一个value,如果没有给New赋值,调用Get时将返回nil.
 New func() interface{}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

poolLocal:

poolLocal是sync.Pool中local或victim指向数组中的元素类型,里面有一个pad数组填充,使得poolLocal的大小构成128字节的整数倍,是为了防止在cache line上分配多个poolLocalInternal从而造成伪共享。

type poolLocal struct {
 poolLocalInternal
 // pad是填充用的,防止伪共享,让整个poolLocal构成128字节的整数倍
 pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte
}
1
2
3
4
5

poolLocalInternal:

它包含private和shared两个字段,private是一个私有对象,只能被拥有它的P使用,而一个P同时只能执行一个G, 所以G访问private不需要加锁,不存在并发冲突。shared是一个双端队列,它既能被与之绑定的P访问和修改,也能被其他的P访问和修改,绑定的P可以从队头加入和出队元素,其他的P只能从队尾出队元素

// poolLocal的内部结构,Pool中的poolLocal是对poolLocalInternal的简单包装
type poolLocalInternal struct {
 // private是一个私有对象,从名字可以看出来,它只能被拥有它的P使用
 // Pool中也说明了,每个P有1个poolLocal,也即有一个poolLocalInternal
 // 这里的private只能被他属于的P使用
 private interface{} // Can be used only by the respective P.
 // shared是一个双端队列,它既能被与之绑定的P访问和修改,也能被其他的P访问和修改
 // 绑定的P可以从对头入队加和出队元素,其他的P只能从队尾出队元素。
 shared poolChain 
}
1
2
3
4
5
6
7
8
9
10

poolChain:

poolChain是一个双向链表的结构体头,它保存着双向链表的头节点和尾节点指针,链表中节点元素类型是poolChainElt。这里需要注意一下,按照我们常规数据结构的理解,head指针指向的位置在链表最左边,tail指针指向的位置在最右边,但是这里刚好是相反的,这里的head指针指向链表的最右边,tail指向最左边。每添加一个元素的时候,添加在head节点的下一个节点,然后将head指向刚添加的节点。

// poolChain是一个双向链表的结构体头,它保存着双向链表的头节点和尾节点指针,poolChainElt
// 是链表中每个节点的类型结构,该结构类型中有2个字段,headTail uint64和 vals []eface,
// headTail作用后面有描述,vals就是存储元素的切片。vals的大小在每个poolChainElt是不同的,
// 相邻的2个直接大小存在着2倍的关系。
type poolChain struct {
 // head只会被1个生产者调用,即Put操作,只在头部添加元素,不需要强同步
 head *poolChainElt
 // tail被消费者调用,从尾部获取元素,可能有多个消费者同时获取,所以操作必须是原子的
 tail *poolChainElt
}
1
2
3
4
5
6
7
8
9
10

poolChainElt:

双向链表中的节点结构,它有一个存储数据的poolDequeue队列,next和prev分别是指向前后节点的指针。poolDequeue中vals字段存放元素,vals是一个固定大小的切片,相邻的两个poolChainElt中的vals大小存在2倍关系,第一个poolChainElt中vals大小是8,可以结合上面pool结构图看,第二个poolChainElt中的size是16,最大值为2^30。

type poolChainElt struct {
 // 存数据的队列,是一个固定大小的切片
 poolDequeue
 // next和prev是双向链表的指针,next被生产者写,消费者读,它的值只会从
 // nil变成非nil, prev被消费者写,生产者读,它的值只会从非nil变成nil
 next, prev *poolChainElt
}
1
2
3
4
5
6
7

poolDequeue:

poolDequeue是一个无锁的、固定大小的,单个生产者、多个消费者的队列。poolDequeue无锁实现是Pool的精髓,巧妙采用CAS去掉了1.13版本之前的有锁实现。它有headTail和vals两个字段,headTail占8个字节,高4字节表示的是head在vals中的下标位置,低4字节表示的是tail在vals中的下标位置。生产者每生产1个数据,head+1,消费者每取走队列中1个数据,tail+1.当tail与head相等的时候,说明队列中没有元素了,当tail+len(vals)=head的时候,说明队列满了。

// poolDequeue是一个无锁的、固定大小的、单个生产者、多个消费者的队列
// 生产者只能从队头向队列添加数据或者从队头取走数据,单个或多个消费者可以
// 可用队尾取走数据
type poolDequeue struct {
 // headTail占8个字节,分为两部分,高位4字节和低位4字节,高4字节表示的
 // 32位int数据描述的是head下标,低4字节表示的是32位int数据描述的是tail
 // 下标,这里的下标说的都是下面vals数组的下标,vals[tail,head)下标范围
 // 内是有数据的,可以被消费者消费。生产者每生产1个数据,head会+1, 消费者
 // 每取走队列中1个数据,tail+1, 当tail赶上head的时候,说明队列中没有数
 // 据了,头部索引存储在最高有效位中,因此我们可以原子地添加它,溢出并不会产生
 // 别的影响。
 headTail uint64
 // vals是一个存储数据的环形队列,数据可以是任何类型,因为定义的类型是interface{}
 // 它的大小必须是2整数次幂。vals[i].typ为nil,表示该下标位置的没有放数据,typ不是
 // nil表示该位置已放有数据。在tail还未设置为i+1和vals[i].typ为非nil前,vals中的
 // 位置i视为还在占用状态。当前消费者取走数据或是生产者读取走数据后,vals[i]将自动被
 // 它们设置为nil.
 vals []eface
}
type eface struct {
 typ, val unsafe.Pointer
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Get实现原理:

Get先从本地 local 的private中获取元素,此private只能被当前的P访问,一个P同时只能运行一个G, 所以直接访问l.privated不需要加锁。如果local.private中没有元素了,尝试从local.shared队列的头部弹出一个元素,如果local.shared中也没有元素了,则从其他P对应的shared中偷取一个,如果其他P对应的shared中也没有元素了,再检查victim中是否有元素,如果还是没有,设置了New方法,将调用New方法产生一个。如果没有设置New方法,将返回nil。

// Get方法从Pool中返回一个元素,并将它从Pool中移除,调用者不要假定放入(Put方法)Pool中元素和从里面
// 取(Get)出来的元素有任何关系,如果Pool中没有缓存的元素了并且P.New没有赋值将会返回nil.否则调用New
// 方法产生一个对象返回
func (p *Pool) Get() interface{} {
 if race.Enabled {
  race.Disable()
 }
 // 把当前的goroutine固定在当前的P上,返回当前的P上的*poolLocal
 l, pid := p.pin()
 // 先从本地local的private字段获取元素,此private只能被当前的P访问
 // 其他P是不能访问的,一个P同一时间只能有1个goroutine在运行,所以
 // 直接访问l.private并不需要加锁
 x := l.private
 l.private = nil
 // private中没有元素
 if x == nil {
  // 尝试从当前的本地local.shared中出队一个元素,x是从队头弹出的。
  x, _ = l.shared.popHead()
  if x == nil {
   // 如果从local.shared也没有拿到,则从其他P中的l.shared中偷1个
            // 如果shared中也没有,产生从victim中获取
   x = p.getSlow(pid)
  }
 }
 runtime_procUnpin()
 if race.Enabled {
  race.Enable()
  if x != nil {
   race.Acquire(poolRaceAddr(x))
  }
 }
 // 走到这里说明,private, local.shared, 其他P中的local.shared, 本地local.victim,
 // 其他P中的local.victim都没有缓存了,如果设置New函数,调用New函数创建一个元素返回
 if x == nil && p.New != nil {
  x = p.New()
 }
 // 走到这里说明没有设置New函数,返回nil
 return x
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

pin方法会调用 runtime_procPin 方法获取当前运行的G绑定到当前的P上,禁止抢占,返回关联P的pid. 然后原子操作取出p.localSize值与Pid进行比较,正常情况下pid是不会比localSize 大,如果不在 ocalSize 范围内,说明程序修改过P的大小,会走到pinSlow处理逻辑。 pin方法会调用runtime_procPin方法获取当前运行的G绑定到当前的P上,禁止抢占,返回关联P的pid. 然后原子操作取出p.localSize值与Pid进行比较,正常情况下pid是不会比localSize大,如果不在localSize范围内,说明程序修改过P的大小,会走到pinSlow处理逻辑。

// 将当前的G定在与它关联的P上,禁止抢占,返回关联P对应的poolLocal对象和P的id
// 调用者必须执行runtime_procUnpin操作当结束对pool操作的时候
func (p *Pool) pin() (*poolLocal, int) {
 // 获取运行当前G,与之关联的P的id
 pid := runtime_procPin()

 // 检查localSize是不是比pid大,正常情况下p.localSize=max(pid)+1
 // 因为pid是P的id,如果P的数量为n,则pid的范围为[0,n), 如果pid的值不在
 // localSize范围内,说明程序修改过P的数量,这里就会走到pinSlow逻辑
 // pinSlow逻辑主要是重新分配p的local和localSize
 s := atomic.LoadUintptr(&p.localSize) // load-acquire
 l := p.local                          // load-consume
 if uintptr(pid) < s {
  return indexLocal(l, pid), pid
 }
 return p.pinSlow()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

pinSlow会再次检查pid是否在localSize范围内,如果不在,将会重新分配poolLocal数组给local, 初始化localSize大小,并将p加入到全局的allPools切片中,allPools维护了程序中所有的sync.Pool对象。

func (p *Pool) pinSlow() (*poolLocal, int) {
 runtime_procUnpin()
 allPoolsMu.Lock()
 defer allPoolsMu.Unlock()
 pid := runtime_procPin()
 // 已经加了allPoolsMu全局锁,这里可以直接读取localSize和local,不存在data race
 s := p.localSize
 l := p.local
 // 再次判断pid的值是否在正常的范围内,刚才前面解除了禁止抢占,等现在再拿到pid的时候
 // 可能不是之前的pid了
 if uintptr(pid) < s {
  return indexLocal(l, pid), pid
 }
 // 将p加入到全局pool allPools中
 if p.local == nil {
  allPools = append(allPools, p)
 }

 // GOMAXPROCS传0返回值是当前可以并发运行的CPU数
 size := runtime.GOMAXPROCS(0)
 // 创建一个新的poolLocal切片,并复制给当前的p
 local := make([]poolLocal, size)
 atomic.StorePointer(&p.local, unsafe.Pointer(&local[0])) // store-release
 atomic.StoreUintptr(&p.localSize, uintptr(size))         // store-release
 // 返回当前的P对应的localPool和pid的值
 return &local[pid], pid
}

// l存储的是local的起始地址,local是一个poolLocal数组,所以根据起始位置+poolLocal的偏移量
// 可以获取到第i个索引中的poolLocal值
func indexLocal(l unsafe.Pointer, i int) *poolLocal {
 lp := unsafe.Pointer(uintptr(l) + uintptr(i)*unsafe.Sizeof(poolLocal{}))
 return (*poolLocal)(lp)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

分析Get中的popHead操作,popHead从poolChain中获取一个数据,优先从头节点head指向的环形队列中获取,如果没有获取到,再从head前一个节点指向的环形队列中获取,直到查询完所有的队列都没有,返回失败。整个处理过程是不断的从链表的head节点向尾节点遍历,对遍历到的每个节点执行popHead, 这个节点类型是poolDequeue, poolDequeue的popHead。

// popHead从poolChain获取一个数据,优先从头部的环形队列中获取,
// 如果没有获取到,在从head前一个节点指向的环形队列中获取,直到
// 查询完所有的队列都没有,返回获取失败false
func (c *poolChain) popHead() (interface{}, bool) {
 d := c.head
 // 从head节点开始查找
 for d != nil {
  if val, ok := d.popHead(); ok {
   return val, ok
  }

  // 继续查找前一个队列,只要d非空即没有查到tail位置,一直
  // 查找,获取到了数据就提前返回
  d = loadPoolChainElt(&d.prev)
 }
 // 走到这里表示没有获取数据,返回失败
 return nil, false
}



// popHead从队列的头部返回一个元素,如果队列为空,第2个返回值将返回false, 注意该函数
// 只能被单个生产者调用,多个生产者即G同时调用,会引发数据竞争
func (d *poolDequeue) popHead() (interface{}, bool) {
 var slot *eface
 for {
  ptrs := atomic.LoadUint64(&d.headTail)
  head, tail := d.unpack(ptrs)
  // 下标tail和head相等了,说明tail追赶上了head,即将vals中的元素取完了
  if tail == head {
   // Queue is empty.
   return nil, false
  }

  head--
  // 构建一个新的uint64,head是-1后的值
  ptrs2 := d.pack(head, tail)
  // 进一步比较headTail和ptrs,如果相同说明没有修改它,将其设置为ptrs2
  // 这时可以放心大胆的对head进行操作,获取里面的元素了,如果这段时间headTail
  // 有人修改,继续进行下一轮循环处理
  if atomic.CompareAndSwapUint64(&d.headTail, ptrs, ptrs2) {
   // 这里可以放心大胆的读取vals中head下标处的元素了
   slot = &d.vals[head&uint32(len(d.vals)-1)]
   break
  }
 }

 // 这里判断slot中的val是不是dequeueNil型,因为在pushHead中有肯能会放入,作占位用
 val := *(*interface{})(unsafe.Pointer(slot))
 if val == dequeueNil(nil) {
  val = nil
 }

 // 将slot填充为nil值,因为popHead和pushHead不会同时被调用,所以这里可以放心大胆对其进行
 // 操作,还有一点是,前面已经重新设置了head值了,popTail已经不可能访问到slot这里,也不用担心
 // popTail与popHead在这里的冲突问题
 *slot = eface{}
 return val, true
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

如果shared中的popHead方法也没有获取到值,将会调下面的getSlow方法。先尝试从其他的P对应的poolLocal中偷一个元素,尝试的顺序是从当前pid+1个索引位置开始的,会对sync.local检查一圈。对每个poolLocal检查的是shared中的元素,看尾部有没有元素,如果有从尾部弹出一个元素。如果检查完所有的poolLocal中的shared都没有找到,接下来将在当前本地poolLocal中的victim的private查找,如果还是没有,就检查它的victim的shared是否有,最后检查其他P对应的poolLocal中victim中的shared是否有数据。

func (p *Pool) getSlow(pid int) interface{} {
 // See the comment in pin regarding ordering of the loads.
 size := atomic.LoadUintptr(&p.localSize) // load-acquire
 locals := p.local                        // load-consume
 // Try to steal one element from other procs.
 // 尝试从其他P中偷一个元素,尝试P的顺序是从当前pid+1个索引位置开始对应的
 // poolLocal中的shared开始查找是否有元素,在shared中的查询方式是从
 // 它的尾部弹出一个元素,如果shared队列中没有,继续尝试下一个位置,直到
 // 循环检查一圈都没有,走victim中检查
 for i := 0; i < int(size); i++ {
  l := indexLocal(locals, (pid+i+1)%int(size))
  if x, _ := l.shared.popTail(); x != nil {
   return x
  }
 }

 size = atomic.LoadUintptr(&p.victimSize)
 if uintptr(pid) >= size {
  return nil
 }
 // 下面尝试从受害中缓存victim中查找是否元素,查找的位置是从pid索引位置开始的poolLocal
 // 产生从它的shared尾部弹出一个元素,如果有就返回,如果没有就尝试下一个位置的poolLocal
 locals = p.victim
 l := indexLocal(locals, pid)
 // 检查victim.private是否有元素,有就返回
 if x := l.private; x != nil {
  l.private = nil
  return x
 }
 for i := 0; i < int(size); i++ {
  l := indexLocal(locals, (pid+i)%int(size))
  if x, _ := l.shared.popTail(); x != nil {
   return x
  }
 }

 // 这里将p.victimSize设置为0,是为了减少后序调用getSlow,不用再一次遍历
 // victim数组了,直接返回nil, 加快处理速度
 atomic.StoreUintptr(&p.victimSize, 0)
 // 尝试了所有的local.shared和victim.private, victim.shared都没有找到,返回nil
 return nil
}



// popTail从poolChain中获取一个数据返回,第一个返回值表示返回的数据,第二个
// 返回值表示是否成功获取到了数据,如果没有获取到,将返回false
func (c *poolChain) popTail() (interface{}, bool) {
 d := loadPoolChainElt(&c.tail)
 // d为nil,表示c.tail还未初始化,比如第一次执行Get操作的时候就会发生,这个时候
 // 还没有Put数据进去,所以获取失败
 if d == nil {
  return nil, false
 }

 for {
  
  // 需要注意这里先要进行loadPoolChainElt操作再执行d.popTail操作,
  // 这两条语句不能颠倒顺序,一般来说,d可能短暂为空,如果在执行pop
  // 且执行失败了,但是在执行前d2为非空。这种情况d永久会为空了,需要将
  // d从poolChain中移除掉
  d2 := loadPoolChainElt(&d.next)

  if val, ok := d.popTail(); ok {
   return val, ok
  }

  if d2 == nil {
   // 走完整个链表了,还是没有获取到数据,直接返回nil,false
   return nil, false
  }

  // 当前tail执行的节点中的环形队列中已经没有数据可以获取了,尝试tail的
  // next节点中获取,因为当前的tail节点永远不会再有数据,所以把它从poolChain
  // 移除掉,下次再执行popTail的时候,就不用检查这个空队列了。
  // 这里采用了CAS操作,因为有多个消费者可能并行执行popTail.
  if atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&c.tail)), unsafe.Pointer(d), unsafe.Pointer(d2)) {
   
   // 将d从poolChain中移除,以便在gc的时候可以将d回收掉,
   // 那为啥在上面的popHead中没有将d从poolChain中移除掉呢?
   // 因为popHead中的d可以留着,在后序的pushHead中可以继续
   // 使用,而popTail中的d永远不会再被使用了,因为没有pushTail操作
   storePoolChainElt(&d2.prev, nil)
  }
  d = d2
 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

上面的popTail对d的处理做了优化,将d从poolChain中移除,以便在gc的时候可以将d回收掉,那为啥在上面的popHead中没有将d从poolChain中移除掉呢?因为popHead中的d可以留着,在后序的pushHead中可以继续使用,而popTail中d永远不会再被使用了,因为没有pushTail操作。

Put实现原理

Put操作处理逻辑比较简单,优先将元素放在本地的private,如果private字段已经有值了,会将元素添加到本地的shared队列中。

![图片](data:image/jpg;base64,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)

GC操作:

sync.Pool中的对象并不是一直保持不释放的。否则对象太多了,会引起内存溢出。Go中的GC对sync.Pool做了专门的处理。在pool.go的init函数里,向GC注册了清理机制,源码如下.poolCleanup 会在 STW 阶段被调用。主要是将 local 和 victim 作交换,那么不至于GC 把所有的 Pool 都清空了,而是需要两个 GC 周期才会被释放

// 注册pool清理回调,在gc的时候,会调用poolCleanup
func init() {
 runtime_registerPoolCleanup(poolCleanup)
}
// 清理pool函数,对oldPools和allPools的操作都是没有加锁的,因为这里执行的时候
// 已经STW了,不存在竞争
func poolCleanup() {
 // oldPools是一个全局变量,是一个保存*sync.Pool的切片,这些
 // 切片元素都是等待被gc清理的,当执行完下面的循环之后,就被清理了
 for _, p := range oldPools {
  p.victim = nil
  p.victimSize = 0
 }
 // allPools是当前新产生的*sync.Pool元素集合,里面存储的元素跟前面的
 // oldPools是不同的,当该轮gc执行的时候,会将allPools中所有的Pool中的
 // local元素移动到victim受害者缓存中,并将local缓存清空
 for _, p := range allPools {
  p.victim = p.local
  p.victimSize = p.localSize
  p.local = nil
  p.localSize = 0
 }
 // 交换oldPools和allPools,处理的很精炼,为什么要这么做呢?
 // 进程起来,运行过程中申请了sync.Pool创建的变量vp,然后执行gc,此时oldPools是空的
 // allPools是非空的,里面装的是vp, 当该函数被调用的时候,将vp中的元素从local移动到victim
 // 即在第一次gc的时候,vp中的元素没有被垃圾回收,接下来执行下面这个语句之后,allPools被清空
 // oldPools里面保存的是vp, 当下次执行gc的时候,oldPools中的内容被清理掉,所以vp中元素生命
 // 周期在两次gc之间都是存活的
 oldPools, allPools = allPools, nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30