# 14.15 漏桶算法

（译者注：翻译遵照原文，但是对于完全没听过这个算法的人来说比较晦涩，请配合代码片段理解）

考虑以下的客户端-服务器结构：客户端协程执行一个无限循环从某个源头（也许是网络）接收数据；数据读取到`Buffer`类型的缓冲区。为了避免分配过多的缓冲区以及释放缓冲区，它保留了一份空闲缓冲区列表，并且使用一个缓冲通道来表示这个列表：`var freeList = make(chan *Buffer,100)`

这个可重用的缓冲区队列（freeList）与服务器是共享的。 当接收数据时，客户端尝试从`freeList`获取缓冲区; 但如果此时通道为空，则会分配新的缓冲区。 一旦消息被加载后，它将被发送到服务器上的`serverChan`通道：

    var serverChan = make(chan *Buffer)

以下是客户端的算法代码：

```go
 func client() {
    for {
        var b *Buffer
        // Grab a buffer if available; allocate if not 
        select {
            case b = <-freeList:
                // Got one; nothing more to do
            default:
                // None free, so allocate a new one
                b = new(Buffer)
        }
        loadInto(b)         // Read next message from the network
        serverChan <- b     // Send to server
        
    }
 }
 
```
服务器的循环则接收每一条来自客户端的消息并处理它，之后尝试将缓冲返回给共享的空闲缓冲区：
```go
func server() {
    for {
        b := <-serverChan       // Wait for work.
        process(b)
        // Reuse buffer if there's room.
        select {
            case freeList <- b:
                // Reuse buffer if free slot on freeList; nothing more to do
            default:
                // Free list full, just carry on: the buffer is 'dropped'
        }
    }
}
```
但是这种方法在`freeList`通道已满的时候是行不通的，因为无法放入空闲`freeList`通道的缓冲区会被“丢到地上”由垃圾收集器回收（故名：漏桶算法）


## 链接

- [目录](directory.md)
- 上一节：[并行化大量数据的计算](14.14.md)
- 下一节：[对Go协程进行基准测试](14.16.md)
