一尘不染

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

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
}

输出:

~ go run jobs.go
Hello, playground
receive close:  0 false
receive close:  0 false

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

为什么上面的代码可以两次关闭?


阅读 267

收藏
2020-07-02

共1个答案

一尘不染

一个通道只能关闭一次,尝试关闭一个已关闭的通道紧急情况。

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

封闭通道上的接收操作始终可以立即进行,在接收到任何先前发送的值之后,得出元素类型的零值

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

您启动了具有无限for循环的第二个goroutine
,无法终止。因此,该循环将继续进行,直到main()在并发的主goroutine中运行的函数返回为止。由于for循环首先从接收jobs,因此它将等待主goroutine将其关闭(此接收操作只能继续进行)。然后,主要goroutine希望从中接收信息done,以便等到第二个goroutine在其上发送一个值。然后,主goroutine是“自由的”以随时终止。由于循环jobs已关闭,因此运行循环的第2个goroutine可能会收到一个附加值,但随后的send
on done会阻塞,因为不再有任何人从该接收(并且它没有缓冲)。

通常使用来完成从通道接收直到关闭的操作for range,如果通道关闭则退出:

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

当然,这将在您的情况下造成死锁,因为永远不会到达循环主体,因为没有人发送任何东西jobs,因此循环永远不会进入其主体以发送值,done而这正是主要goroutine等待的。

2020-07-02