【问题标题】:Closing channels in Go在 Go 中关闭通道
【发布时间】:2016-08-09 04:17:41
【问题描述】:

我正在学习通道在 Go 中的工作方式,并且偶然发现了关闭通道的问题。这是 A Tour of Go 中的一个修改示例,它生成 n-1 个斐波那契数并通过通道发送它们,而通道容量的最后一个“元素”未使用。

func fibonacci(n int, c chan int) {
    x, y := 0, 1
    for i := 0; i < n-1; i++ {
        c <- x
        x, y = y, x+y

    }
    // close(c) // It's commented out on purpose
}

func main() {
    n := 10
    c := make(chan int, n)
    go fibonacci(n, c)

    for i := 0; i < n; i++ {
        _, ok := <-c
        fmt.Println(ok)
    }
}

问题是我得到了:

致命错误:所有 goroutine 都处于休眠状态 - 死锁!

当我不关闭频道时。究竟是什么导致了僵局?不关闭通道,为什么不能在其容量边界内接收?

【问题讨论】:

  • 仅供参考,如果您希望关闭频道,读取频道的惯用方式是for v := range c {,因为您不需要检查 OK - 当频道关闭时,for 循环将退出.如果您不希望频道关闭,请使用for { select { case v := &lt;- c: ... 。这样您就可以拥有额外的case 条件,例如超时、监控取消令牌等,并在检测到退出条件时打破 for 循环。

标签: go channel


【解决方案1】:

您正在将 n 个值写入通道(从 0 到 n-1),但正在尝试读取 n+1来自通道的值(从 0 到 n)。如果不显式关闭通道,main 函数将永远等待最后一个值。

究竟是什么导致了死锁?

n 次迭代后,运行 fibonacci 函数的 goroutine 将退出。在这个 goroutine 退出之后,你的程序中唯一剩下的 goroutine 就是 main goroutine,这个 goroutine 正在等待一些数据被写入 c 通道——因为没有其他 goroutine 可能永远存在将数据写入此通道,它将永远等待。这正是错误消息试图告诉你的内容:"all goroutines ("all" is just "one", here)are sleep".

main 函数中的 _, ok := &lt;- c 调用只会在 c 通道关闭后立即停止阻塞(因为从通道读取是阻塞的,这需要从另一个 goroutine 完成)。当通道关闭时,main 函数会从通道中读取剩余数据(当通道为缓冲通道时)

【讨论】:

    【解决方案2】:

    主循环中的 For 循环期望通道中有 n 次通信,但在 func fibonacci 中只产生 n-1

    func fibonacci(n int, c chan int) {
        x, y := 0, 1
        for i := 0; i < n; i++ { //here
            c <- x
            x, y = y, x+y
    
        }
        // close(c) // It's commented out on purpose
    }
    

    应该可以 http://play.golang.org/p/zdRuy14f9x

    【讨论】:

    • 我知道,这也是故意的,将频道容量的一个“元素”留空。
    • 但它会导致死锁。在 main 中,您永远在频道中等待 n 的消息,因为您不发送它。
    猜你喜欢
    • 2019-01-13
    • 2015-10-02
    • 1970-01-01
    • 1970-01-01
    • 2011-05-26
    • 2018-05-22
    • 2021-03-14
    • 2018-02-18
    • 1970-01-01
    相关资源
    最近更新 更多