为什么在写入关闭的通道时会出现panic ?

虽然人们可以使用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上运行"从关闭的通道读取"

向一个可能关闭的通道写入数据更复杂,因为如果在通道关闭时try 写入数据,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上运行"Write to Closed Channel"(写入关闭的通道

据我所知,没有一个更简单的习语可以让你在一个可能封闭的渠道中写作而不惊慌失措.为什么不呢?这种读写不对称行为背后的原因是什么?

推荐答案

Go Language Spec:

对于通道c,内置函数close(C)记录不再 值将在通道上发送.如果c是a,则是错误的. 仅接收通道.发送或关闭关闭的通道会导致 运行时的panic .关闭NIL通道也会导致运行时死机. 在调用Close之后,并且在以前发送的任何值都已 接收,则接收操作将返回 无阻塞的通道类型.多值接收操作 返回一个接收到的值,并指示 频道关闭了.

如果您写入关闭的通道,您的程序将会死机.如果您真的想这样做,可能会有catch this error with recover个,但是处于不知道您正在写入的通道是否打开的情况下,通常是程序中存在错误的迹象.

以下是一些引述:

这里有一个动机:

通道"Close"实际上只是在 频道.它是一个特殊的值,它promise 不会再有更多的值 被送go .try 在通道上发送已设置的值 Closed将会死机,因为实际发送值将违反 由Close提供担保.因为收盘只是一种特殊的 发送,通道关闭后也不允许发送.

下面是另一个例子:

通道关闭的唯一用途是向读卡器发出信号,表示有

(伊恩·兰斯·泰勒)

--

下面是另一个例子:

关闭频道会将其作为资源释放.没有更多的意义,那就是 关闭通道的次数比关闭文件的次数还多 描述符多次使用,或释放挡路分配的内存 很多次.这样的动作意味着代码被 destruct 了,这就是为什么 关闭关闭的通道会引发panic .

(罗布·派克)

--

来源:Go design detail rationale question - channel close

Go相关问答推荐

GORM Find(&;Room)操作使用空数据而不是实际数据填充 struct

如何使用我的 struct 化日志(log)记录格式使人panic ?

减少在围棋中映射DTO时的重复代码量

需要类型[]*structpb.Value(GCP Golang客户端库;aiPlatform)

如何在链接中写入链接

未实现的 desc = 未知服务 pb.AuthService 我的简单身份验证服务器上出现错误

如何绕过深层 xml,没有嵌套循环?

Go 中的 protobuf FieldMask 解组

如何将Golang测试用例的测试覆盖率值与特定阈值进行比较

是否可以在调试期间在 VSCode 中预览 github.com/shopspring/decimal 值?

如何将 base64 编码的公钥转换为 crypto.PublicKey 或 ecdsa.PublicKey

Gorm delete with clauses sqlmock 测试

Go http.FileServer 给出意外的 404 错误

获取不带类型参数的泛型 struct 的类型名称

从动态输入中提取字符串,其中部分字符串可能不存在

如何根据 Go 中第二次出现的分隔符拆分字符串?

无法建立连接,因为目标机器主动拒绝它 Golang

如果服务器在客户端的 gRPC 中不可用,则等待的方法

Golang - 无法从 pipped Windows 命令中获取结果

不能使用 *T 类型的变量作为参数类型