【问题标题】:Deadlock after attempting to print values of channel using 'range'尝试使用“范围”打印通道值后出现死锁
【发布时间】:2015-01-27 00:01:01
【问题描述】:

这是我在Go Playground的代码

package main

import (
    "fmt"
)

func sum_up(my_int int, cs chan int) {
    my_sum := 0
    for i := 0; i < my_int; i++ {
        my_sum += i
    }
    cs <- my_sum
}

func main() {

    my_channel := make(chan int)
    for i := 2; i < 5; i++ { 
        go sum_up(i, my_channel)
    }


    for ele := range my_channel {
        fmt.Println(ele)
    }  

    //fatal error: all goroutines are asleep - deadlock!

    fmt.Println("Done")

}

结果:

1
3
6
fatal error: all goroutines are asleep - deadlock!

而且我不明白是什么导致了错误。我的理解是,在我的函数sum_up 中,我正在向my_channel 添加新值。为什么我尝试打印值后会出现问题?由于我看到打印了 1,3,6,这意味着所有 goroutines 都已成功完成。

此外,如果块尝试打印通道的值

    for ele := range my_channel {
        fmt.Println(ele)
    }

已删除,然后我没有收到错误消息。所以它包含了导致错误的块,但是为什么呢?

【问题讨论】:

    标签: go channel goroutine


    【解决方案1】:

    空通道上的 for-range 将阻塞,直到有要从通道读取的元素或通道关闭。

    这是一个版本,它使用sync.WaitGroup 来说明有多少 goroutines 仍然处于活动状态。所有 goroutine 完成后,通道关闭,for-range 循环存在。

    https://play.golang.org/p/ZnLYxLMNdF

    package main
    
    import (
        "fmt"
        "sync"
    )
    
    func sum_up(my_int int, cs chan int, wg *sync.WaitGroup) {
        my_sum := 0
        for i := 0; i < my_int; i++ {
            my_sum += i
        }
        cs <- my_sum
        wg.Done()
    }
    
    func main() {
        wg := &sync.WaitGroup{}
        my_channel := make(chan int)
        for i := 2; i < 5; i++ {
            wg.Add(1)
            go sum_up(i, my_channel, wg)
        }
    
        // Run a goroutine that will monitor how many sum_up are running.
        go func(cs chan int, wg *sync.WaitGroup) {
            wg.Wait()
            close(cs)
        }(my_channel, wg)
    
        for ele := range my_channel {
            fmt.Println(ele)
        }
    
        //fatal error: all goroutines are asleep - deadlock!
    
        fmt.Println("Done")
    
    }
    

    【讨论】:

      【解决方案2】:

      当您在通道上使用range 时,它将永远等待值或直到通道关闭。这是死锁,因为当最后一个值写入 my_channel 时,它将永远等待一个永远不会到来的值。

      这是一个稍微修改的变体,显示了如何干净地离开范围:https://play.golang.org/p/YDlM8EcRnx

      【讨论】:

      • 感谢您的回答,但是您的代码是否与我的相似,我正在创建三个不同的线程,而您只创建一个。我错过了什么吗?
      【解决方案3】:

      for range chan 当 chan 收到关闭信号时退出。你必须在某个地方close(my_channel),否则循环将永远等待。

      【讨论】:

        猜你喜欢
        • 2017-12-14
        • 1970-01-01
        • 2014-11-18
        • 1970-01-01
        • 2019-09-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多