Golang 之 sync.Pool揭秘
sync.Pool 主要通过减少GC来提升性能,是Goroutine并发安全的
sync.Pool使用
初始化Pool实例,可以通过配置new方法来声明Pool元素创建的方法
bufferpool := &sync.Pool { New: func() interface {} { println("Create new instance") return struct{}{} } }
申请和释放,申请会返回Pool中已经存在的对象,如果没有就初始化一个对象,Pool中的对象何时释放外界是不清楚的
buffer := bufferPool.Get()
bufferPool.Put(buffer)
当有频繁创建的对象时,而且不用关注对象的状态,就用Pool,可以避免大量实例的创建与释放
// 用来统计实例真正创建的次数 var numCalcsCreated int32 // 创建实例的函数 func createBuffer() interface{} { // 这里要注意下,非常重要的一点。这里必须使用原子加,不然有并发问题 atomic.AddInt32(&numCalcsCreated, 1) buffer := make([]byte, 1024) return &buffer } func main() { // 创建实例 bufferPool := &sync.Pool{ New: createBuffer, } numWorkers := 1024 * 1024 var wg sync.WaitGroup wg.Add(numWorkers) for i := 0; i < numWorkers; i++ { go func() { defer wg.Done() // 申请一个 buffer 实例 buffer := bufferPool.Get() _ = buffer.(*[]byte) // 释放一个 buffer 实例 defer bufferPool.Put(buffer) }() } wg.Wait() fmt.Printf("%d buffer objects were created", numCalcsCreated) } 输出 8 buffer objects were created or 不固定 7 buffer objects were created
将bufferPool.Get变为buffer := createBuffer(),将输出1048576 buffer objects were created在程序运行时,进程消耗内存非常大,同时在内部runtime GC回收时会很影响性能
sync.Pool线程安全吗?
sync.Pool本身是线程安全的,get和put方法都是线程安全的,但是new函数不是线程安全的,上例中用原子加就是这个原因,不用原子加,可能最后的数字会比实际的小一点,但是在当下多核机器下几乎看不到区别
为什么 sync.Pool
不适合用于像 socket 长连接或数据库连接池?
Pool 池里的元素随时可能释放掉,释放策略完全由 runtime 内部管理;
Get 获取到的元素对象可能是刚创建的,也可能是之前创建好 cache 住的。使用者无法区分;
Pool 池里面的元素个数你无法知道;
总结:
sync.Pool 本质用途是增加临时对象的重用率,减少 GC 负担,
不能对 Pool.Get 出来的对象做预判,有可能是新的(新分配的),有可能是旧的(之前人用过,然后 Put 进去的),也无法去判读啊Pool中元素个数
sync.Pool 本身的 Get, Put 调用是并发安全的,sync.New
指向的初始化函数会并发调用,
当用完一个从 Pool 取出的实例时候,一定要记得调用 Put,否则 Pool 无法复用这个实例,通常这个用 defer 完成,
本文来自博客园,作者:LeeJuly,转载请注明原文链接:https://www.cnblogs.com/peterleee/p/13405915.html