前言
Go 语言标准库中最重要的同步工具:
- 互斥锁 开箱即用
- 读写锁 开箱即用
- 条件变量 (sync.Cond) 不是开箱即用, sync.NewCond 函数需要lock变量的指针作为参数
- 原子操作 (sync/atomic.Value) 开箱即用
- sync.WaitGroup 开箱即用
- context.Context 开箱即用
- sync.Pool 不是开箱即用
sync.Pool 基础特性
- 结构体类型
- 并不是开箱即用的
- sync.Pool类型的New字段代表着创建临时对象的函数,它的类型没有参数但是有唯一结果的类型函数:即:func() interface{}
临时对象池为何会被及时的清理掉
- sync 包再被初始化的时候,会向 Go 语言运行是系统注册一个函数,这个函数的功能就是清除所有已创建的临时对象池中的值(池清理函数)
- 池清理函数一旦注册到 Go 原因运行时系统,后者每次即将执行垃圾回收时都会执行该函数
- sync包中还有一个包级私有的全局变量(元素类型为 *sync.Pool 的切片),用于存储当前程序中的临时对象池(池汇总列表)
- 在临时对象池的Put方法或者Get方法第一次被调用的时候,该池就会被添加到池汇总列表中
- 池清理函数访问的正是所有正在被真正使用的临时对象池
临时对象池的数据结构
- 存储私有临时对象的字段 private
- 代表共享对象列表的字段 shared
- sync.Mutex类型的嵌入字段
sync.Pool中的本地池与各个G的对应关系
- 一个goroutine要想真正运行就必须与某个P产生关联
- 程序在调用临时对象池的Put方法或者Get方法的时候,总会先试图从该临时对象池的本地池列表中获取与之对应的本地池(依据的就是与当前goroutine关联的那个P的ID)
临时对象池是怎样利用内部数据结构存取值
- 临时对象池的Put方法总是先试图把新的临时对象存储到本地池的private字段
- 若Private字段已经存在其它值是,该方法会去访问本地池的shared字段
- 一个shared字段原则上可被任何goroutine访问到
- 相比private字段只能被当前goroutine访问到
- 使用Get方法获取临时对象时,若发现本地池的private字段没有值时,就会在互斥锁的保护下访问shared字段
- 若本地池的share字段没值,则会遍历本地池列表的其它本地池的share字段
- 即便如此也是有可能拿不到任何结果值(如所有的临时对象池刚被打清洗)
- 这时Get方法就会调用创建临时对象的函数,初始化一个临时对象池
package mainimport ("bytes""fmt""io""sync")// bufPool 代表存放数据块缓存区的临时对象池var bufPool sync.Pool// Buffer 代表了一个简易的数据缓冲区的接口type Buffer interface {// Delimiter 用于获取数据块之间的定界符Delimiter() byte// Write 用于写一个数据块Write(contents string) (err error)// Read 用于读一个数据块Read() (contents string, err error)// Free 用于释放当前的缓冲区Free()}// myBuffer 代表了数据块缓冲区的一种实现type myBuffer struct {buf bytes.Bufferdelimiter byte}func (b *myBuffer) Delimiter() byte {return b.delimiter}func (b *myBuffer) Write(contents string) (err error) {if _, err = b.buf.WriteString(contents); err != nil {return}return b.buf.WriteByte(b.delimiter)}func (b *myBuffer) Read() (contents string, err error) {return b.buf.ReadString(b.delimiter)}func (b *myBuffer) Free() {bufPool.Put(b)}// delimiter 代表预定义的定界符var delimiter = byte('\n')func init() {bufPool = sync.Pool{New: func() interface{} {return &myBuffer{delimiter: delimiter}},}}// GetBuffer 用于获取一个数据块缓存区func GetBuffer() Buffer {return bufPool.Get().(Buffer)}func main() {buf := GetBuffer()defer buf.Free()buf.Write("A pool is a set of temporary objects that" + "may be individualy saved and retrieved.")buf.Write("A Pool is safe for use by multiple goroutines simultaneously.")buf.Write("A Pool must not be copied after first use.")fmt.Println("The data blocks in buffer:")for {block, err := buf.Read()if err != nil {if err == io.EOF {break}panic(fmt.Errorf("unexpected error: %s", err))}fmt.Print(block)}}
