前言
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 main
import (
"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.Buffer
delimiter 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)
}
}