背景

Go 编程中关于 channel 的使用非常重要,本文简要罗列一些要点,仅供参考。

有缓冲通道和无缓冲通道

本章节摘抄于《Go in Action》 一书(这本书的几张图当时解决了我入门时一些疑惑的地方)

无缓冲通道(unbuffered channel)

无缓冲通道(unbuffered channel)是指在接收前没有能力保存任何值的通道。这种类型的通道要求发送 goroutine 和接收 goroutine 同时准备好,才能完成发送和接收操作。如果两个 goroutine 没有同时准备好,通道会导致先执行发送或接收操作的 goroutine 阻塞等待这种对通道进行发送和接收的交互行为本身就是同步的。其中任意一个操作都无法离开另一个操作单独存在。如下图所示:

  • 1:两个 goroutine 都到达通道,但哪个都没有开始执行发送或者接收;

  • 2:左侧的 goroutine 将它的手伸进了通道,这模拟了向通道发送数据的行为,这时,这个 goroutine 会在通道中被锁住,直到交换完成;

  • 3:右侧的 goroutine 将它的手放入通道,这模拟了从通道里接收数据,这个 goroutine 一样也会在通道中被锁住,直到交换完成;

  • 4 - 5:进行交换;

  • 6:释放 goroutine;

有缓冲通道(buffered channel)

有缓冲通道(buffered channel)是一种在接收前能存储一个或者多个值的通道。这种类型的通道并不强求 goroutine 之间必须同时完成发送和接收。只有在通道中没有要接收的值,接收动作才会被阻塞。只有在通道没有可用缓冲区容纳被发送的值时,发送动作才会被阻塞。这导致有缓冲的通道和无缓冲的通道之间的一个很大的差异:无缓冲通道保证进行发送和接收的 goroutine 会在同一时间进行数据交换;有缓冲通道没有这种保证。如下图所示:

  • 1:右侧 goroutine 正从通道接收一个值;

  • 2:右侧 goroutine 独立完成接收值的动作,而左侧的 goroutine 正在发送一个新值到通道里;

  • 3:左侧的 goroutine 还在向通道发送新值,而右侧的 goroutine 正从通道接收另一个值,这个步骤是异步的;

  • 4:所有的发送和接收都完成,而通道里还有几个值,也有一些剩余空间;

单向通道和双向通道

Go 的类型系统提供了 单向通道类型仅仅导出发送或接收操作,如:

  • chan<- int:只发不收;

  • <-chan int:只收不发,close 会在编译时报错;

违反这个原则会在编译时被检查出来。

我们普通定义的 channel 一般都是双向通道,在任何赋值操作中,将双向通道转换为单向通道都是允许的,但是反过来是不行的。一旦有一个像 chan<- int 的单向通道,是无法通过它获取到引用同一数据结构的 chan int 类型的。

参考资料