sync.Pool在Go中引入,用于重复使用临时对象以降低对垃圾回收器的压力。历史上,开发者使用互斥锁创建自己的对象池,但这导致了复杂的代码和潜在的同步错误。sync.Pool是标准库中的线程安全结构,旨在首次访问时生成对象,存储并随后将该对象返回到池中以供重复使用。
问题是,当短命对象数量较多时(例如,HTTP服务器中的缓冲区),系统通常会花费大量时间在分配上。使用sync.Pool可以选择性地缓存对象,不保证它们保留在内存中,但通过减少垃圾回收的次数来提高性能。
解决方案是使用Pool处理临时、均匀、快速使用的结构(例如,bytes.Buffer),通过Put方法返回对象并通过Get提取对象。对象可以在任何时刻从池中被移除(例如,在启动GC时),因此不适合长期存储。
代码示例:
import ( "sync" "bytes" "fmt" ) var bufPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, } func main() { b := bufPool.Get().(*bytes.Buffer) b.Reset() b.WriteString("hello, world!") fmt.Println(b.String()) bufPool.Put(b) // 一定要返回 }
关键特点:
sync.Pool可以用于长期存储状态吗?
不可以,池中的任何对象都可能在任何时刻被系统删除(在GC或负载降低时)。Pool仅用于goroutine之间的临时存储。
Pool保证返回的确实是之前放入的同一个对象吗?
不。Pool返回任何合适的对象,必要时还会创建新对象。用户与Pool对象之间不应存在绑定。
归还对象到Pool之前需要清理/重置对象吗?
是的,否则下一个线程可能会获得带有先前数据残余的“脏”对象。
负面案例
开发者将客户端状态保存在Pool中以便重新连接到聊天。启动GC后,连接消失,用户失去数据。
优点:
缺点:
积极案例
将用于marshal/unmarshal JSON的Buffer对象放入Pool中,并用于批处理消息。处理后,对象被清理并返回到池中,从而减少分配次数。
优点:
缺点: