28

为什么 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上运行“写入封闭频道”

据我所知,没有更简单的习惯用法可以在不惊慌的情况下写入可能关闭的频道。为什么不?读写之间这种不对称行为背后的原因是什么?

4

1 回答 1

31

来自Go 语言规范

对于通道 c,内置函数 close(c) 记录不会在通道上发送更多值。如果 c 是只接收通道,则这是一个错误。发送到或关闭已关闭的通道会导致运行时恐慌。关闭 nil 通道也会导致运行时恐慌。在调用 close 之后,并且在接收到任何先前发送的值之后,接收操作将返回通道类型的零值,而不会阻塞。多值接收操作返回接收到的值以及通道是否关闭的指示。

如果你写到一个封闭的频道,你的程序会恐慌。如果您真的想这样做,您可能会通过恢复来捕获此错误,但是在您不知道您正在写入的通道是否打开的情况下,通常表明程序中存在错误。

一些报价:

这是一个动机:

通道“关闭”实际上只是在通道上发送特殊值。这是一个特殊的值,承诺不再发送任何值。在通道关闭后尝试在通道上发送值会恐慌,因为实际发送值会违反 close 提供的保证。由于关闭只是一种特殊的发送,因此在通道关闭后也是不允许的。

这是另一个:

通道关闭的唯一用途是向读者发出信号,表明没有更多的值即将到来。这仅在存在单一值来源或多个来源协调时才有意义。没有一个合理的程序可以让多个 goroutine 关闭一个通道而不进行通信。这意味着多个 goroutine 会知道没有更多的值要发送——如果它们不通信,它们如何确定呢?

(伊恩·兰斯·泰勒)

--

这是另一个:

关闭通道会将其作为资源释放。与多次关闭文件描述符或多次释放分配的内存块相比,多次关闭通道没有任何意义。这样的行为意味着代码被破坏了,这就是为什么关闭一个关闭的通道会触发一个恐慌。

(罗伯·派克)

--

资料来源:Go 设计详细原理问题 - 频道关闭

于 2016-01-20T11:43:13.177 回答