在 Go 语言中,channel 缓冲区的大小影响着程序的行为。这里简单用饲养员喂猴子吃香蕉的故事来说明三种情况下的区别。先做一下前提说明:
- 饲养员每次只能拿一只香蕉去喂猴子
- 饲养员可以把香蕉放盘子里,猴子会自己拿走吃
- 如果没有盘子,则饲养员必须要亲自喂猴子吃
- 一只盘子只能放一只香蕉
缓冲区大小如何影响 channel 的行为
情况一
无缓冲:没有盘子。
饲养员每次去喂猴子,必须留在猴子身边喂完才能去做别的事情。如果猴子在忙,饲养员必须干站着等,等猴子出现再喂。
情况二
缓冲等于 1 :有一只盘子。
饲养员拿香蕉去喂猴子时,不用管猴子是不是在忙,可以直接把香蕉放在盘子里,回去忙自己的(去拿新的香蕉)。但如果饲养员来的时候发现盘子里之前放的香蕉还没有吃,这时候饲养员就要等猴子过来取走之后,再把带来的香蕉放盘子里离开。
情况三
缓冲大于 1 :有多只盘子。
只要有空盘子,饲养员就可以放下香蕉离开;只有当没有空盘子的时候才需要等待。
合理控制缓冲区的大小
channel 的缓冲大小要么是 1,要么是无缓冲的,不应该用大于 1 的缓冲。这是出于什么原因呢?
这就要谈到一个保证机制的问题,即,什么样的 channel 机制使得发送端在发送一条数据的时候,能保证之前的数据有被消费?
先给出结论:对于无缓冲的 channel,能够保证消息被接收;对于缓冲区大小为 1 的 channel,能获得一个延迟的保证;对于缓冲区大于 1 的 channel,不能得到保证。
还是用上面饲养员与猴子的例子,现在想象,无论有多少只盘子,饲养员每次带香蕉来的时候,至多只能看到一个空盘子。他不关心、也不了解之前投放的香蕉猴子有没有吃完,他只关心是不是有个空盘子,这样他就可以扔下香蕉走人。
于是这就带来一个问题——既然 channel 缓冲区大小等于 1 时,已经能够实现并行的处理了,那么当缓冲区更大的时候,还能带来什么额外好处呢?
也许有人会说,如果饲养员喂香蕉的速度大于猴子吃香蕉的速度,那么饲养员就可以早点把活干完。可是别忘了,饲养员并不是真的人,他只是一个程序,即使所有盘子满了,他也是不能回去休息的,他必须不停地工作。
那这么看来,盘子数量多实在是没有什么好处,因为盘子满了之后饲养员还是要站在那里干等。
也就是说,如果饲养员不能保证(guarantee)猴子吃完了香蕉,他就没必要再回去拿新的香蕉,那样还会占用更多内存资源呢。因此,在大多数情况下,缓冲区的大小都不应超过 1。
注:上面只讨论了一个饲养员和一只猴子的情况,这个问题还可以更加复杂,比如一个饲养员多只猴子(fan-out),多个饲养员一只猴子(fan-in),多个饲养员多只猴子等等。