目录
channel关闭原则(The Channel Closing Principle)
本文翻译自《How to Gracefully Close Channels》
几天前,我写了一篇文章解释了go语言中channel使用方法。这篇文章在reddit和HN上获得了很多肯定,但也有一些关于channel设计细节的批评。
对channels的设计和规则的批评如下:
- 在不修改channel状态的情况下,没有简单通用的方法来检查channel是否关闭。
- 关闭一个已经关闭的的channel会导致panic,所以如果关闭者不知道channel是否关闭,那么关闭channel是危险的。
- 向关闭的channel发送数据会导致panic,因此,如果发送者不知道channel是否关闭,则向channel发送数据是危险的。
这些批评看起来合情合理(事实并非如此)。是的,确实没有内置函数来检查channel是否已经关闭。
如果没有任何数据被发送到(并且将被发送到)channel,那么确实有一种简单的方法可以检查channel是否关闭。该方法已在上一篇文章中介绍。这里,为了更好的连贯性,在下面的例子中再次列出该方法,如下:
package main
import "fmt"
type T int
func IsClosed(ch <-chan T) bool {
select {
case <-ch:
return true
default:
}
return false
}
func main() {
c := make(chan T)
fmt.Println(IsClosed(c)) // false
close(c)
fmt.Println(IsClosed(c)) // true
}
如上所述,这不是检查channel是否关闭的通用方法。
事实上,即使有一个内置函数来检查channel是否已关闭,它的作用也会非常有限,就像内置函数len()用于检查存储在channel缓冲区中的当前值数量一样。因为,在对此类函数的调用返回后,被检查channel的状态可能已经发生了变化,因此返回值已经无法反映刚被检测channel的最新状态。尽管如果调用closed(ch) 返回true,可以停止向chan ch发送值,但如果调用closed(ch) 返回false,则关闭channel或继续向channel发送值是不安全的。
一、channel关闭原则(The Channel Closing Principle)
使用go语言channel的一个一般原则是,不要从接收者关闭channel;如果channel有多个并发发送者,也不要关闭channel。换句话说,如果发送者是channel的唯一发送者,那么我们应该只关闭发送者goroutine协程中的channel。
(下面,我们将上述原则称为channel关闭原则。)
当然,这并不是关闭channel的普遍原则。通用原则是不要重复关闭channel或向关闭的channel发送值。如果我们可以保证没有goroutine将关闭并将值发送到非关闭的非nil的channel,那么goroutine可以安全地关闭channel。但实际上,由接收者或者chan