Go 的 goroutine 能够让繁琐的并发变得简单易用。Go 不能没有 channel 就像西方不能失去耶路撒冷。Channel 非常神奇,即使是经验丰富的工程师也会被它绊倒。下面让我们来谈谈开发人员在使用 Go 中的 Channel 时常犯的一些错误,以及如何避免这些错误。
Deadlocks
死锁是使用 channel 时可能遇到的最频繁的问题。当一个程序在等待永远不会发生的事情时被卡住,就会出现死锁。想象一下,你试图将数据发送到一个 channel,但另一边却没有人接收数据。你的程序就这样停在那里,什么也不做。
来看看这段代码:
1 | func main() { |
该程序之所以挂起的原因是,代码试图向一个未缓冲通道发送数值,而没有任何 goroutine 可以接收该数值。解决方法很简单:使用 goroutine 发送值。下面是解决方法:
1 | func main() { |
通过生成一个 goroutine 来处理发送,就能确保主 goroutine 可以接收数据,程序就不会卡住。
Buffered Channels: 不要滥用缓冲区
当你想发送多个值而又不想立即阻塞时,缓冲通道是个不错的选择,但你需要小心使用。缓冲通道就像是数据的等待室。
举个通俗的例子,假设你正在经营一家小邮局。等候区只有一把椅子。这就像一个容量为 1 的 buffer channel。现在,如果有两个人来邮寄包裹,会发生什么情况呢?
第一个人坐下,没问题。但当第二个人到达时,他们就只能站在外面了,因为没有更多的空间。这正是本代码示例中发生的情况:
1 | func main() { |
我们的候车室只能容纳一个 “人”(在这里是一个号码),否则就会堵塞。
但如果我们把候车室变得更大呢?
1 | func main() { |
现在,我们的候车室里有了两把椅子!两位 “顾客 “都能坐得舒服了,我们的小邮局也能顺利运转了。
所以当我们使用 buffer channel 的时候,要确保缓冲区足够大,否则可能会导致死锁。
Closing Channels: 不要忘记关闭
另一个高频的的错误是在使用完 channel 后忘记关闭。如果不关闭 channel,等待从 channel 接收数据的程序可能会一直等待并且永远不会到来的数据。
1 | func main() { |
这段代码将打印 0 到 4 的数字,但随后会无限期挂起,因为 range 循环在等待更多数据。channel 从未关闭,因此循环不知道何时停止。
解决方法是什么?发送完数据后关闭通道:
1 | func main() { |
现在,当循环收到通道关闭信号时,它就知道要停止了。