Go语言 sync.Pool 应用详解

sync.Pool 除了最常见的池化提升性能的思路,最重要的是减少 GC 。
常用于一些对象实例创建昂贵的场景。注意,Pool 是 Goroutine 并发安全的。

一、sync.Pool 适应场景

sync.Pool 本质用途是增加临时对象的重用率,减少 GC 负担;

sync.Pool 中保存的元素有如下特征:

  • Pool 池里的元素随时可能释放掉,释放策略完全由 runtime 内部管理;
  • Get 获取到的元素对象可能是刚创建的,也可能是之前创建好 cache 的,使用者无法区分;
  • Pool 池里面的元素个数你无法知道;
    所以,只有的你的场景满足以上的假定,才能正确的使用 Pool 。

划重点:临时对象。像 socket 这种带状态的、长期有效的资源是不适合 Pool 的。

二、sync.Pool 使用方法

1.初始化 Pool 实例 New

创建一个 Pool 实例,关键一点是配置 New 方法,声明 Pool 元素创建的方法。源码1.15版本的 Pool.go 声明 Pool结构如下:

// A Pool must not be copied after first use.
type Pool struct {
        noCopy noCopy

        local     unsafe.Pointer // local fixed-size per-P pool, actual type is [P]poolLocal
        localSize uintptr        // size of the local array

        victim     unsafe.Pointer // local from previous cycle
        victimSize uintptr        // size of victims array

        // 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{}   // New是一个方法、返回值为 接口
}

如何给此方法赋值呢?

bufPool := &sync.Pool { 
	New: func() interface{} {
		//函数体内容
		return struct {}{}
	}
}

2.申请对象 Get

Get 方法会返回 Pool 已经存在的对象;如果没有就使用New方法创建.

3.释放对象 Put

对象或资源不用时,调用 Put 方法把对象或资源放回池子,池子里面的对象啥时候真正释放是由 go_runtime进行回收,是不受外部控制的。

三、sync.Pool 实例

package main

import (
        "fmt"
        "sync"
        "sync/atomic"
        "time"
)

var createNum int32

func createBuffer() interface{} {
        atomic.AddInt32(&createNum, 1)
        buffer := make([]byte, 1024)
        return buffer
}

func main() {
        bufferPool := &sync.Pool{New: createBuffer,}

        workerPool := 1024 * 1024
        var wg sync.WaitGroup
        wg.Add(workerPool)

        for i := 0; i < workerPool; i++ {
                go func(){
                        defer wg.Done()
                        buffer := bufferPool.Get()
                        _ = buffer.([]byte)
                        defer bufferPool.Put(buffer)
                        //buffer := createBuffer()
                        //_ = buffer.([]byte)
                }()
        }
        wg.Wait()
        fmt.Printf(" %d buffer objects were create.\n",createNum)
        time.Sleep(5 * time.Second)
}

运行效果

robot@ubuntu:~/gomod/src/sync$ go run syncPool.go 
 4 buffer objects were create.
robot@ubuntu:~/gomod/src/sync$ go run syncPool.go 
 4 buffer objects were create.

使用 pool 申请 1024 *1024 个 1k buffer 缓存,而实际调用 createBuffer 创建次数只有4次;
创建次数与机器的运行环境有关,未必相同。

你可能感兴趣的:(golang,专栏)