当我在做一些 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两次?
一个通道只能关闭一次,试图关闭一个关闭的通道会导致恐慌。
但接收从封闭通道没有限制,从关闭信道接收:
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 等待的值。