mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-28 05:46:36 +08:00
74 lines
2.1 KiB
Markdown
74 lines
2.1 KiB
Markdown
# 使用 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 的并发原语以及需要更多控制池行为的场景仍然很有价值。
|