1、part1 一个例子
? ?
p := pool.Get().(*Person)
?? ?fmt.Printf("第一次从pool里拿对象, %+v \n", p)
?? ?p.Name = "HanMeiMei"
?? ?pool.Put(p)
?? ?fmt.Printf("pool里已经有一个对象, %+v \n", pool.Get().(*Person))
?? ?fmt.Printf("pool里已经没有对象了, %+v \n", pool.Get().(*Person))
?? ?L()
? ?2、part2 golang里fmt.Printf如何使用sync.Pool
fmt.Printf中Printf对应的函数是:
?
func Printf(format string, a ...interface{}) (n int, err error) {
?? ??? ??? ?return Fprintf(os.Stdout, format, a...)
?? ??? ?}
?? ??? ?继续看Fprintf函数:
?? ??? ?func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
?? ??? ??? ?p := newPrinter()
?? ??? ??? ?p.doPrintf(format, a)
?? ??? ??? ?n, err = w.Write(p.buf)
?? ??? ??? ?p.free()
?? ??? ??? ?return
?? ??? ?}
?? ??? ?Fprintf第一个参数是io.Writer,Printf函数里传的是os.Stdout,相当于直接输出到标准输出。这里的newPrinter就是调用的Pool, ?? ??? ?拿到pp指针之后,首先是做了一些format操作。并且将p.buf的内容写到w,调用free()函数将p重新归还到pool里。归还之前将p的部分字段重置,以便于其它代码再次拿到这个对象时是“干净”的。
?其中newPrinter对应的函数体是 ?? ???
?func newPrinter() *pp {
?? ??? ??? ?p := ppFree.Get().(*pp)
?? ??? ??? ?p.panicking = false
?? ??? ??? ?p.erroring = false
?? ??? ??? ?p.wrapErrs = false
?? ??? ??? ?p.fmt.init(&p.buf)
?? ??? ??? ?return p
?? ??? ?}
函数体里ppFree是一个变量,对应一个sync.Pool ?? ???
?var ppFree = sync.Pool{
?? ??? ??? ?New: func() interface{} { return new(pp) },
?? ??? ?}
结构体pp保存了一个打印对象的状态并且被sync.Pool重用以防止出现内存分配瓶颈。
接下来我们看一下pool_test.go的TestPoolNew函数 在src/sync/pool_test.go ?? ??? ??? ?
func TestPoolNew(t *testing.T) {
?? ??? ??? ??? ?// disable GC so we can control when it happens.
?? ??? ??? ??? ?// 首先设置了GC=-1,就是不去做GC
?? ??? ??? ??? ?defer debug.SetGCPercent(debug.SetGCPercent(-1))
?? ??? ??? ??? ?i := 0
?? ??? ??? ??? ?p := Pool{
?? ??? ??? ??? ??? ?New: func() interface{} {
?? ??? ??? ??? ??? ??? ?i++
?? ??? ??? ??? ??? ??? ?return i
?? ??? ??? ??? ??? ?},
?? ??? ??? ??? ?}
?? ??? ??? ??? ?// 此时pool里为空,连续调用两次Get,会返回1、2
?? ??? ??? ??? ?if v := p.Get(); v != 1 {
?? ??? ??? ??? ??? ?t.Fatalf("got %v; want 1", v)
?? ??? ??? ??? ?}
?? ??? ??? ??? ?if v := p.Get(); v != 2 {
?? ??? ??? ??? ??? ?t.Fatalf("got %v; want 2", v)
?? ??? ??? ??? ?}
?? ??? ??? ??? ?// 防止goroutine被抢占,目的是保护接下来的Put和Get操作都是同一个P的池子
?? ??? ??? ??? ?Runtime_procPin()
?? ??? ??? ??? ?p.Put(42)
?? ??? ??? ??? ?if v := p.Get(); v != 42 {
?? ??? ??? ??? ??? ?t.Fatalf("got %v; want 42", v)
?? ??? ??? ??? ?}
?? ??? ??? ??? ?Runtime_procUnpin()
?? ??? ??? ??? ?if v := p.Get(); v != 3 {
?? ??? ??? ??? ??? ?t.Fatalf("got %v; want 3", v)
?? ??? ??? ??? ?}
?? ??? ??? ?}
?? ??? ?type PoolDequeue interface {
?? ??? ??? ?PushHead(val interface{}) bool
?? ??? ??? ?PopHead() (interface{}, bool)
?? ??? ??? ?PopTail() (interface{}, bool)
?? ??? ?}
PoolDequeue 是一个双端队列,可以从头入队元素也可以从头、尾出队元素。
?3、?gin框架也用到了sync.Pool
?? ?github.com\gin-gonic\gin\gin.go
func New() *Engine {
?? ?...
?? ?engine.pool.New = func() interface{} {
?? ??? ??? ?return engine.allocateContext()
?? ??? ?}
?? ?}
?? ?func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
?? ??? ?c := engine.pool.Get().(*Context)
?? ??? ?c.writermem.reset(w)
?? ??? ?c.Request = req
?? ??? ?c.reset()
?? ??? ?engine.handleHTTPRequest(c)
?? ??? ?engine.pool.Put(c)
?? ?}
?? ?
?? ? ?
|