Files
OpenFlare/.agents/skills/go-concurrency/references/BUFFER-POOLING.md
T
2026-08-04 11:40:32 +08:00

74 lines
2.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 使用 Channel 的缓冲池
使用有缓冲 channel 作为空闲列表来复用已分配的缓冲区,避免重复分配。这种"泄漏缓冲"模式使用带 `default` 的 `select` 进行非阻塞操作。
> **来源**:Effective Go
```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 会回收)
}
}
```
## 工作原理
1. **非阻塞接收**:客户端尝试从 `freeList` 获取缓冲区。如果为空,`default` 分支运行并分配新缓冲区。
2. **非阻塞发送**:服务端尝试归还缓冲区。如果 `freeList` 已满,`default` 分支运行,缓冲区被丢弃等待垃圾回收。
3. **有限内存**:channel 容量(100)限制了池中缓冲区的数量,防止无限增长。
当分配成本较高且缓冲区复用有益,但你不希望在池空或池满时出现阻塞行为时,这种模式非常有用。
## 何时使用
- 高频分配相似大小的对象
- 分配开销影响性能的代码路径
- 需要限制内存使用量的场景
## 生产环境替代方案
对于生产代码,考虑使用 `sync.Pool`,它提供类似功能并与垃圾收集器有更好的集成:
```go
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 的并发原语以及需要更多控制池行为的场景仍然很有价值。