Golang基本同步原语sync.Pool
sync.Pool
是 Go 标准库 sync
包中的一个结构,用于管理一组可重用的对象池。通过复用对象,减少内存分配和垃圾回收的开销,从而提升程序的性能和效率。以下是对 sync.Pool
的详细讲解,包括其使用案例和使用场景。
sync.Pool 的大小是可伸缩的,高负载时会动态扩容,存放在池中的对象如果不活跃了会被自动清理。
1. sync.Pool 的功能和作用
sync.Pool
主要用于在多个 goroutine 之间高效地共享和重用临时对象。它通过保持一个池子,存储已经分配但当前不使用的对象。当需要新的对象时,首先从池子中获取,如果池子中没有可用的对象,则创建新的对象。使用完成后,将对象归还池子,以便后续复用。
sync.Pool
的主要优势在于减少内存分配和垃圾回收的开销,尤其在需要频繁创建和释放临时对象的场景下,能够显著提升程序的性能。
2.sync.Pool核心方法
sync.Pool
提供了以下几个核心方法:
New()
pool := &sync.Pool{
New: func() interface{} {
// 返回一个新的对象
return new(Object)
},
}
- 作用:初始化一个新的
sync.Pool
实例,New
是一个工厂函数,用于创建池子中对象的工厂方法。 - 场景:当池子中没有可用对象时,会调用
New
方法创建新的对象。
Get()
obj := pool.Get()
- 作用:从池子中获取一个可用的对象。如果池子中有对象,直接返回;如果没有,调用
New
方法创建新的对象。 - 返回值:返回一个
interface{}
类型的对象,需要进行类型断言。
Put()
pool.Put(obj)
- 作用:将一个对象归还到池子中,供后续复用。
- 注意:归还的对象应该处于初始状态,避免残留数据导致潜在的数据竞争或不一致问题。
Close()
pool.Close()
- 作用:关闭池子,防止新的
Get
和Put
操作。关闭后,已经获取的对象可以继续使用,但池子不会接受新的归还。 - 场景:在程序不再需要池子时,调用
Close()
方法进行清理。
使用场景
sync.Pool
适用于以下场景:
- 频繁创建和释放临时对象
- 例如,处理大量的网络请求时,使用池子管理临时的缓冲区或上下文对象,减少内存分配和垃圾回收的开销。
- 减少内存分配开销
- 对于需要频繁创建和销毁的对象,使用池子可以复用已有对象,减少内存分配和回收的次数,提升性能。
- 提升并发性能
- 在多个 goroutine 之间共享对象时,池子提供了一种高效的方式,减少了竞争和内存分配的开销。
package main_test
import (
"bytes"
"sync"
"testing"
)
var bufferPool = sync.Pool{New: func() any {
return bytes.NewBuffer(make([]byte, 0, 1024))
}}
var data = make([]byte, 100000)
func BenchmarkBufferWithPool(b *testing.B) {
for n := 0; n < b.N; n++ {
buf := bufferPool.Get().(*bytes.Buffer)
buf.Write(data)
buf.Reset()
bufferPool.Put(buf)
}
}
func BenchmarkBuffer(b *testing.B) {
for n := 0; n < b.N; n++ {
var buf *bytes.Buffer = bytes.NewBuffer(nil)
buf.Write(data)
}
}
这个例子创建了一个 bytes.Buffer
对象池,而且每次只执行一个简单的 Write
操作,存粹的内存搬运工,耗时几乎可以忽略。而内存分配和回收的耗时占比较多,因此对程序整体的性能影响更大。