Golang-sync.Pool使用及原理


Pool

​ 你想看一本书,需要的时候开始印刷,看完了就卖废品,1000个人想看就得印刷1000本书,卖1000本废品。这明显太不绿色了,你想了想如果建一个图书馆,里面放几本书,大家想看的时候去借,看完了再还回去,这明显高效了不少。

​ 程序员发现了你用图书馆的方式合理的利用了资源,于是照猫画虎发明了连接池、线程池、协程池、内存池,各种各样的池都是想解决同类的问题:创建连接、线程等都相对比较消耗资源,通过池存一写已经新建的连接,线程…需要的时候拿去用,不用了就再还回来。借与还的模式,节省了不少资源,大家都说好。

​ Golang的sync.Pool,对象池。采用对象池来创建对象,增加对象的重复利用率,使用的时候就不必在堆上重新创建对象可以节省开销。

使用

sync.Pool对外提供三个方法:New,Get,Put。

var pool *sync.Pool

type Book struct {
    Name string
}

func init() {
    pool = &sync.Pool{
        New: func() interface{} {
            return new(Book)
        },
    }
}

func main() {
    book := pool.Get().(*Book)
    fmt.Println(book) // &{}
    book.Name = "Go Programming"
    pool.Put(book)
    fmt.Println(pool.Get().(*Book)) // &{Go Programming}
    fmt.Println(pool.Get().(*Book)) // &{}
}

源码分析

  • 结构体
type Pool struct {
    noCopy noCopy // 禁止拷贝

    local     unsafe.Pointer // [P]poolLocal poolLocal数组指针
    localSize uintptr        // 数组大小

    victim     unsafe.Pointer // GC处理并不直接将allPools的object直接进行GC处理,而是保存到oldPools,等到下一个GC周期到了再处理。
    victimSize uintptr

    // 当pool没有缓存对象的时候,会调用New方法生成一个新的对象
    New func() interface{}
}

type poolLocal struct {
    poolLocalInternal
    pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte
}
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 poolChain struct {
    // head is the poolDequeue to push to. This is only accessed
    // by the producer, so doesn't need to be synchronized.
    head *poolChainElt

    // tail is the poolDequeue to popTail from. This is accessed
    // by consumers, so reads and writes must be atomic.
    tail *poolChainElt
}

  • Get
func (p *Pool) Get() interface{} {
    if race.Enabled { // 不允许竞态检测
        race.Disable()
    }
    l, pid := p.pin() // 获取一个poolLocal
    x := l.private
    l.private = nil
    if x == nil {
        // Try to pop the head of the local shard. We prefer
        // the head over the tail for temporal locality of
        // reuse.
        x, _ = l.shared.popHead()
        if x == nil {
            x = p.getSlow(pid)
        }
    }
    runtime_procUnpin()
    if race.Enabled {
        race.Enable()
        if x != nil {
            race.Acquire(poolRaceAddr(x))
        }
    }
    if x == nil && p.New != nil { // 没拿到 new一个
        x = p.New()
    }
    return x
}

func (p *Pool) pin() *poolLocal {
    pid := runtime_procPin()
    s := atomic.LoadUintptr(&p.localSize) // load-acquire
    l := p.local                          // load-consume
    if uintptr(pid) < s {
        return indexLocal(l, pid)
    }
    return p.pinSlow()
}

评论
  目录