为什么 Go 的通道可以关闭两次?

she*_*fly 2 channel go

当我在做一些 go 练习代码时,我遇到了一个问题,一个通道可以像这样关闭两次:

// jobs.go

package main

import (
   "fmt"
)

func main() {
    fmt.Println("Hello, playground")
    jobs := make(chan int, 5)
    done := make(chan bool)

    go func() {
        for {
            j,more := <-jobs

            fmt.Println("receive close: ", j, more)
            done <- true   
        }
    }()

    close(jobs)
    <- done
}
Run Code Online (Sandbox Code Playgroud)

输出:

~ go run jobs.go
Hello, playground
receive close:  0 false
receive close:  0 false
Run Code Online (Sandbox Code Playgroud)

但是当我手动关闭频道两次时,我得到了panic: close of closed channel.

为什么上面的代码可以接收close两次?

icz*_*cza 7

一个通道只能关闭一次,试图关闭一个关闭的通道会导致恐慌。

接收从封闭通道没有限制,从关闭信道接收:

关闭通道上的接收操作总是可以立即进行,在接收到任何先前发送的值后产生元素类型的零值

Go 应用程序一直运行到它的主 goroutine 运行(在“正常”情况下),或者从另一个角度来看:Go 应用程序在其主 goroutine 终止时终止,即main()函数返回。它不会等待其他非maingoroutine 完成。

你开始了一个无限for循环的第二个 goroutine ,没有办法终止。因此,该循环将继续main()运行,直到在并发主 goroutine 中运行的函数返回为止。由于for循环首先从 接收jobs,它等待主 goroutine 关闭它(这个接收操作才能继续)。然后主 goroutine 想要从 接收done,以便等待,直到第二个 goroutine 在其上发送一个值。然后主 goroutine 可以“自由”随时终止。运行循环的第二个 goroutine 可能会收到一个额外的值,jobs因为它已经关闭,但随后的发送done将阻塞,因为没有人再接收它(并且它是无缓冲的)。

从通道接收直到它关闭通常是使用 完成的for range,如果通道关闭,它会退出:

for j := range jobs {
    fmt.Println("received: ", j)
    done <- true
}
Run Code Online (Sandbox Code Playgroud)

当然,在您的情况下,这会导致死锁,因为永远不会到达循环主体,因为没有人在 上发送任何内容jobs,因此循环永远不会进入其主体以发送done主 goroutine 等待的值。