为什么Go在向封闭通道写入时会感到恐慌?
虽然可以使用value, ok := <-channel
习惯用法从通道读取,并且因此可以测试ok结果是否命中关闭的通道:
// reading from closed channel
package main
import "fmt"
func main() {
ch := make(chan int, 1)
ch <- 2
close(ch)
read(ch)
read(ch)
read(ch)
}
func read(ch <-chan int) {
i,ok := <- ch
if !ok {
fmt.Printf("channel is closed\n")
return
}
fmt.Printf("read %d from channel\n", i)
}
输出:
read 2 from channel
channel is closed
channel is closed
在Playground上运行"从关闭通道读取"
向一个可能关闭的通道写操作要复杂得多,因为如果你只是在通道关闭的情况下写操作,Go语言就会崩溃:
//writing to closed channel
package main
import (
"fmt"
)
func main() {
output := make(chan int, 1) // create channel
write(output, 2)
close(output) // close channel
write(output, 3)
write(output, 4)
}
// how to write on possibly closed channel
func write(out chan int, i int) (err error) {
defer func() {
// recover from panic caused by writing to a closed channel
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
fmt.Printf("write: error writing %d on channel: %v\n", i, err)
return
}
fmt.Printf("write: wrote %d on channel\n", i)
}()
out <- i // write on possibly closed channel
return err
}
输出:
write: wrote 2 on channel
write: error writing 3 on channel: send on closed channel
write: error writing 4 on channel: send on closed channel
在Playground上运行"写入关闭通道"
据我所知,没有一个更简单的习惯用法可以让你不慌不忙地写入一个可能关闭的通道,为什么不呢?读写之间这种不对称行为背后的原因是什么?
2条答案
按热度按时间mnowg1ta1#
从Go语言规范:
对于通道c,内置函数close(c)记录不再在通道上发送值。如果c是只接收通道,则为错误。发送到或关闭已关闭的通道会导致运行时异常。关闭nil通道也会导致运行时异常。调用close之后,并且在接收到任何以前发送的值之后,接收操作将返回通道类型的零值,而不会阻塞。多值接收操作返回一个接收值以及通道是否关闭的指示。
如果你写入一个关闭的通道,你的程序将会死机,如果你真的想这么做,你可以用recover捕捉这个错误,但是如果你不知道你正在写入的通道是否是打开的,这通常是程序有bug的信号。
一些引语:
这里有一个动机:
通道"close"实际上只是在通道上发送一个特殊值。它是一个承诺不再发送值的特殊值。在通道关闭后试图在通道上发送值将出现异常,因为实际发送值将违反close提供的保证。由于close只是一种特殊类型的发送,因此在通道关闭后也不允许这样做。
下面是另一个例子:
channel close的唯一用途是向读取器发出信号,通知它没有更多的值要输入,这只有在只有一个值源时才有意义。或者当多个源协调时,没有一个合理的程序可以让多个goroutine关闭一个通道而不进行通信,这意味着多个goroutine知道没有更多的值可以发送--如果它们不知道,它们怎么能确定呢?我们不能交流吗?
(Ian兰斯·泰勒)
下面是另一个例子:
关闭通道会释放它作为资源。多次关闭通道与多次关闭文件描述符或多次释放已分配内存块一样没有意义。此类操作意味着代码已损坏,这就是关闭已关闭通道会触发死机的原因。
(Rob派克)
来源:Go design detail rationale question - channel close
oewdyzsn2#
Go通道是为一个writer和多个reader设计的。因此,writer必须创建、共享和关闭通道。在某种程度上,writer拥有通道。尽管您可以在一个通道上有多个writer(不建议/不推荐)。