前言

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方法第一次被调用的时候,该池就会被添加到池汇总列表中
  • 池清理函数访问的正是所有正在被真正使用的临时对象池

临时对象池的数据结构

  • 临时对象池有一个多层的数据结构(本地池列表)
  • 本地池列表是一个数组,数组的长度与Go语言调度器中P的数量相同

    本地池列表中每个池都含三个字段

  1. 存储私有临时对象的字段 private
  2. 代表共享对象列表的字段 shared
  3. sync.Mutex类型的嵌入字段

sync.Pool中的本地池与各个G的对应关系

  1. 一个goroutine要想真正运行就必须与某个P产生关联
  2. 程序在调用临时对象池的Put方法或者Get方法的时候,总会先试图从该临时对象池的本地池列表中获取与之对应的本地池(依据的就是与当前goroutine关联的那个P的ID)

临时对象池是怎样利用内部数据结构存取值

  1. 临时对象池的Put方法总是先试图把新的临时对象存储到本地池的private字段
  2. 若Private字段已经存在其它值是,该方法会去访问本地池的shared字段
  3. 一个shared字段原则上可被任何goroutine访问到
  4. 相比private字段只能被当前goroutine访问到
  5. 使用Get方法获取临时对象时,若发现本地池的private字段没有值时,就会在互斥锁的保护下访问shared字段
  6. 若本地池的share字段没值,则会遍历本地池列表的其它本地池的share字段
  7. 即便如此也是有可能拿不到任何结果值(如所有的临时对象池刚被打清洗)
  8. 这时Get方法就会调用创建临时对象的函数,初始化一个临时对象池
  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "sync"
  7. )
  8. // bufPool 代表存放数据块缓存区的临时对象池
  9. var bufPool sync.Pool
  10. // Buffer 代表了一个简易的数据缓冲区的接口
  11. type Buffer interface {
  12. // Delimiter 用于获取数据块之间的定界符
  13. Delimiter() byte
  14. // Write 用于写一个数据块
  15. Write(contents string) (err error)
  16. // Read 用于读一个数据块
  17. Read() (contents string, err error)
  18. // Free 用于释放当前的缓冲区
  19. Free()
  20. }
  21. // myBuffer 代表了数据块缓冲区的一种实现
  22. type myBuffer struct {
  23. buf bytes.Buffer
  24. delimiter byte
  25. }
  26. func (b *myBuffer) Delimiter() byte {
  27. return b.delimiter
  28. }
  29. func (b *myBuffer) Write(contents string) (err error) {
  30. if _, err = b.buf.WriteString(contents); err != nil {
  31. return
  32. }
  33. return b.buf.WriteByte(b.delimiter)
  34. }
  35. func (b *myBuffer) Read() (contents string, err error) {
  36. return b.buf.ReadString(b.delimiter)
  37. }
  38. func (b *myBuffer) Free() {
  39. bufPool.Put(b)
  40. }
  41. // delimiter 代表预定义的定界符
  42. var delimiter = byte('\n')
  43. func init() {
  44. bufPool = sync.Pool{
  45. New: func() interface{} {
  46. return &myBuffer{delimiter: delimiter}
  47. },
  48. }
  49. }
  50. // GetBuffer 用于获取一个数据块缓存区
  51. func GetBuffer() Buffer {
  52. return bufPool.Get().(Buffer)
  53. }
  54. func main() {
  55. buf := GetBuffer()
  56. defer buf.Free()
  57. buf.Write("A pool is a set of temporary objects that" + "may be individualy saved and retrieved.")
  58. buf.Write("A Pool is safe for use by multiple goroutines simultaneously.")
  59. buf.Write("A Pool must not be copied after first use.")
  60. fmt.Println("The data blocks in buffer:")
  61. for {
  62. block, err := buf.Read()
  63. if err != nil {
  64. if err == io.EOF {
  65. break
  66. }
  67. panic(fmt.Errorf("unexpected error: %s", err))
  68. }
  69. fmt.Print(block)
  70. }
  71. }