0
点赞
收藏
分享

微信扫一扫

sync.Pool

1.介绍

1.1sync.Pool是什么

​sync.pool​​Go1.3​发布的一个特性,它是一个临时对象存储池程序员使用时不能对Pool中的元素个数做假定

1.2为什么使用sync.Pool

1.保存和复用临时对象

2.减少内存分配

3.降低GC压力

1.3sync.Pool方法有哪些

//初始化Pool实例
New func() interface{}

//申请对象
//Get方法会返回Pool中已经存在的对象,如果不存在,就调用New初始化
func (p *Pool) Get() interface{}

//释放对象
//使用完对象后,要放回赤字中。注意:池子中的对象什么时候真正释放是不受外界控制的。
func (p *Pool) Put(x interface{})

1.4思考?

1.为什么使用Pool,而不是在运行的时候直接实例话对象呢?

2.sync.Pool是并发安全的吗?

3.为什么sync.Pool不适合用于像socket长链接活数据库连接池?

2.案例

package main

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

// 用来统计实例真正创建的次数
var numCalcsCreated int32

// 创建实例的函数
func createBuffer() interface{} {
// 这里要注意下,非常重要的一点。这里必须使用原子加,不然有并发问题;
atomic.AddInt32(&numCalcsCreated, 1)
buffer := make([]int, 0, 100)
return &buffer
}

func main() {
// 创建实例
bufferPool := &sync.Pool{
New: createBuffer,
}

// 多 goroutine 并发测试
numWorkers := 30
var wg sync.WaitGroup
wg.Add(numWorkers)

for i := 0; i < numWorkers; i++ {
routineNum := i
go func() {
defer wg.Done()
// 申请一个 buffer 实例
buffer := bufferPool.Get()
li := buffer.(*[]int)
*li = append(*li, routineNum, routineNum+1, routineNum+2)
fmt.Println(li)

// 释放一个 buffer 实例
defer func() {
//使用完了恢复原样
*li = []int{}
bufferPool.Put(buffer)
}()
}()
}
wg.Wait()
fmt.Printf("%d buffer objects were created.\n", numCalcsCreated)
}

sync.Pool_初始化

sync.Pool_临时对象_02


上面例子我们看到,一次创建了9次buffer,一次创建了11次buffer。

2.1减小对象的创建

如果我们不使用Pool来申请实例,而是直接申请,对象的创建数量和并发Worker数量相同,==30

所以使用Pool能够减少对象的创建,减少内存分配,今儿也减小GC对临时对象的回收次数。

sync.Pool_复用_03

2.2Pool是并发安全的

sync.Pool只是本身的Pool数据结构是并发安全的,并不是说Pool.New函数是线程安全的。

所在在New函数中,我们使用了 atomic.AddInt32(&numCalcsCreated, 1) 来避免并发问题。

2.3为什么sync.Pool不适合用于socket或数据库连接池呢?

因为,我们不能对 sync.Pool 中保存的元素做任何假设,以下事情是都可以发生的:

  1. Pool 池里的元素随时可能释放掉,释放策略完全由 runtime 内部管理;
  2. Get 获取到的元素对象可能是刚创建的,也可能是之前创建好 cache 住的。使用者无法区分;
  3. Pool 池里面的元素个数你无法知道;

sync.Pool 本质用途是增加临时对象的重用率,减少 GC 负担。划重点:临时对象。所以说,像 socket 这种带状态的,长期有效的资源是不适合 Pool 的。

总结:

  1. sync.Pool 本质用途是增加临时对象的重用率,减少 GC 负担;
  2. 不能对 Pool.Get 出来的对象做预判,有可能是新的(新分配的),有可能是旧的(之前人用过,然后 Put 进去的);
  3. 不能对 Pool 池里的元素个数做假定,你不能够;
  4. sync.Pool 本身的 Get, Put 调用是并发安全的,sync.New 指向的初始化函数会并发调用,里面安不安全只有自己知道;
  5. 当用完一个从 Pool 取出的实例时候,一定要记得调用 Put,否则 Pool 无法复用这个实例,通常这个用 defer 完成;

举报

相关推荐

0 条评论