var strPool = sync.Pool{New: func() interface{} {return "test str"},}func main() {str := strPool.Get()fmt.Println(str)strPool.Put(str)}
type Pool struct {noCopy noCopylocal unsafe.Pointer // 数组指针,指向[P]poolLocallocalSize uintptr // 大小为Pvictim unsafe.Pointer // 用于存放“幸存者”victimSize uintptr // “幸存者”size// New optionally specifies a function to generate// a value when Get would otherwise return nil.// It may not be changed concurrently with calls to Get.New func() interface{}}type poolLocalInternal struct {private interface{} // Can be used only by the respective P.shared poolChain // Local P can pushHead/popHead; any P can popTail.}type poolLocal struct {poolLocalInternal// Prevents false sharing on widespread platforms with// 128 mod (cache line size) = 0 .pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte}
local这里面真正的是[P]poolLocal其中P就是GPM模型中的P,有多少个P数组就有多大,也就是每个P维护了一个本地的poolLocal。
poolLocal里面维护了一个private一个shared,看名字其实就很明显了,private是给自己用的,而shared的是一个队列,可以给别人用的。注释写的也很清楚,自己可以从队列的头部存然后从头部取,而别的P可以从尾部取。
victim这个从字面上面也可以知道,幸存者嘛,当进行gc的stw时候,会将local中的对象移到victim中去,也就是说幸存了一次gc。
func (p *Pool) Get() interface{} {// ......l, pid := p.pin()// Step1: 先直接获取自己的private,如果有,直接返回x := l.privatel.private = nilif x == nil {// Step2: 如果private为空,就从自己的shared随便取一个x, _ = l.shared.popHead()if x == nil {x = p.getSlow(pid)}}runtime_procUnpin()// ......if x == nil && p.New != nil {// Step5: 找了一圈都没有,自己New一个x = p.New()}return x}func (p *Pool) getSlow(pid int) interface{} {size := atomic.LoadUintptr(&p.localSize)locals := p.localfor i := 0; i < int(size); i++ {l := indexLocal(locals, (pid+i+1)%int(size))// Step3: 从其它的P中随便偷一个出来if x, _ := l.shared.popTail(); x != nil {return x}}size = atomic.LoadUintptr(&p.victimSize)if uintptr(pid) >= size {return nil}// Step4: 从“幸存者”中找一个,找的逻辑和前面的一样,先private,再sharedlocals = p.victiml := indexLocal(locals, pid)if x := l.private; x != nil {l.private = nilreturn x}for i := 0; i < int(size); i++ {l := indexLocal(locals, (pid+i)%int(size))if x, _ := l.shared.popTail(); x != nil {return x}}atomic.StoreUintptr(&p.victimSize, 0)return nil}
如果 private 不是空的,那就直接拿来用;
如果 private 是空的,那就先去本地的shared队列里面从头 pop 一个;
如果本地的 shared 也没有了,那 getSlow 去拿,其实就是去别的P的 shared 里面偷,偷不到回去 victim 幸存者里面找;
如果最后都没有,那就只能调用 New 方法创建一个了。
func (p *Pool) Put(x interface{}) {if x == nil {return}// ......l, _ := p.pin()if l.private == nil {l.private = xx = nil}if x != nil {l.shared.pushHead(x)}runtime_procUnpin()// ......}
如果 private 没有,就放在 private;
如果 private 有了,那么就放到 shared 队列的头部。
G表示Goroutine协程,M表示thread线程,P表示processor处理器;
M是G运行的实体,P的作用就是将G分配到M上;
一个P有个本地队列,专门用于存放G。
再回顾一下GMP核心的调度流程:
当程序运行时,P会从本地队列中随机取一个G,然后给到M运行;
当P的本地队列没有G时,会从全局对列中找一批G,然后放到自己的本地队列,然后再取出G;
当本地队列为空时,P会从其它的P的本地队列中抢一批G,然后放到自己的本地队列,然后再取出G;
当没有抢到时,M就自动挂起来,不运行了。
12.4 总结