mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-28 05:46:36 +08:00
2.1 KiB
2.1 KiB
使用 Channel 的缓冲池
使用有缓冲 channel 作为空闲列表来复用已分配的缓冲区,避免重复分配。这种"泄漏缓冲"模式使用带 default 的 select 进行非阻塞操作。
来源:Effective Go
var freeList = make(chan *Buffer, 100) // 有缓冲 channel 作为空闲列表
// 客户端:从空闲列表获取缓冲区或分配新的
func getBuffer() *Buffer {
select {
case b := <-freeList:
return b // 复用已有缓冲区
default:
return new(Buffer) // 空闲列表为空;分配新缓冲区
}
}
// 服务端:如有空间则将缓冲区归还空闲列表,否则丢弃
func putBuffer(b *Buffer) {
b.Reset() // 为重用做准备
select {
case freeList <- b:
// 缓冲区已归还空闲列表
default:
// 空闲列表已满;丢弃缓冲区(GC 会回收)
}
}
工作原理
- 非阻塞接收:客户端尝试从
freeList获取缓冲区。如果为空,default分支运行并分配新缓冲区。 - 非阻塞发送:服务端尝试归还缓冲区。如果
freeList已满,default分支运行,缓冲区被丢弃等待垃圾回收。 - 有限内存:channel 容量(100)限制了池中缓冲区的数量,防止无限增长。
当分配成本较高且缓冲区复用有益,但你不希望在池空或池满时出现阻塞行为时,这种模式非常有用。
何时使用
- 高频分配相似大小的对象
- 分配开销影响性能的代码路径
- 需要限制内存使用量的场景
生产环境替代方案
对于生产代码,考虑使用 sync.Pool,它提供类似功能并与垃圾收集器有更好的集成:
var bufferPool = sync.Pool{
New: func() any {
return new(Buffer)
},
}
func getBuffer() *Buffer {
return bufferPool.Get().(*Buffer)
}
func putBuffer(b *Buffer) {
b.Reset()
bufferPool.Put(b)
}
sync.Pool 的优势:
- 垃圾收集期间自动清理
- 无需管理池大小
- 天生线程安全
- 高并发下性能更好
基于 channel 的方式对于理解 Go 的并发原语以及需要更多控制池行为的场景仍然很有价值。