关于sync.Pool的疑问

想在pool的基础上做一个限制池中对象数量的功能,但发现还是多次执行pool.New

package main

import (
    "bytes"
    "log"
    "sync"
    "time"
)

const MaxFrameSize = 5000

func main() {
    for i := 0; i < 10; i++ {
        // 多个协程想从pool中拿到对象
        go func() {
            c := getBuf()
            putBuf(c)
            log.Println("put done")
        }()
    }

    time.Sleep(3 * time.Second)
}

var bufPool = sync.Pool{
    New: func() interface{} {
        log.Println("alloc")
        return bytes.NewBuffer(make([]byte, 0, MaxFrameSize))
    },
}

var bufPoolChan = make(chan bool, 1)

func getBuf() *bytes.Buffer {
    bufPoolChan <- true
    b := bufPool.Get().(*bytes.Buffer)
    b.Reset()
    return b
}

func putBuf(b *bytes.Buffer) {
    bufPool.Put(b)
    <-bufPoolChan
}

期望是只执行一次NewBuffer,也就是只打印一次alloc。 实际上每次执行,会打印多次alloc,有大佬帮忙分析一波,如何限制pool中的对象数量吗?

已邀请:

sync.Pool的源代码里说了,pool里的对象随时都有可能被自动移除,并且没有任何通知。sync.Pool的数量是不可控制的。

Pool调用New与线程调度有关,Pool内部有一个localPool的数组,每个P对应其中一个localPool,在当前P执行goroutine的时候,优先从当前的localPool的private变量取,娶不到在从shared列表里面取,再取不到就尝试从别的P的localPool的shared里面偷一个。最后实在取不到就New一个。

由于你的bufPoolChan限制基本上10个goroutine就在两个P后面排队轮流执行,所以alloc就会出现两次,后面的基本就是从这两个localPool的private取出来的。

如果取消这个限制,10个goroutine很快就被分配到10个P上去了,对应就有10个localPool,10次每次取private都取不到,取shared列表也取不到,别的localPool也没得偷,就会New10次,alloc就会出现10次。

mooy

赞同来自: gowalker

runtime.GOMAXPROCS(1)你可以在开头加上这个试一下,看new了几个,然后改为2,再看一下。 看put的一段代码

func (p *Pool) Put(x interface{}) {
    if x == nil {
        return
    }
    l := p.pin()
    if l.private == nil {
        l.private = x
        x = nil
    }
    runtime_procUnpin()
    if x != nil {
        l.Lock()
        l.shared = append(l.shared, x)
        l.Unlock()
    }
}

首先会给private,然后才会分配给shared。private是每个P私有的,所以即使已经把对象放回池子里去,别的P也取不到。

gosky

赞同来自:

我觉得可以用chann解决

要回复问题请先登录注册