【问题标题】:Deadlocks with buffered channels in GoGo中缓冲通道的死锁
【发布时间】:2021-12-25 06:25:13
【问题描述】:

我遇到以下代码fatal error: all goroutines are asleep - deadlock!

我使用缓冲通道是否正确?如果您能给我指点,我将不胜感激。不幸的是,我已经走投无路了。

func main() {
    valueChannel := make(chan int, 2)
    defer close(valueChannel)
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go doNothing(&wg, valueChannel)
    }

    for {
        v, ok := <- valueChannel
        if !ok {
            break
        }
        fmt.Println(v)
    }
    wg.Wait()
}

func doNothing(wg *sync.WaitGroup, numChan chan int) {
    defer wg.Done()
    time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
    numChan <- 12
}

【问题讨论】:

    标签: go channel


    【解决方案1】:

    收到所有值后,&lt;- valueChannel 上的主要 goroutine 阻塞。关闭通道以解除对主 goroutine 的阻塞。

    func main() {
        valueChannel := make(chan int, 2)
        var wg sync.WaitGroup
        for i := 0; i < 10; i++ {
            wg.Add(1)
            go doNothing(&wg, valueChannel)
        }
    
        // Close channel after goroutines complete.
        go func() {
            wg.Wait()
            close(valueChannel)
        }()
    
        // Receive values until channel is closed. 
        // The for / range loop here does the same
        // thing as the for loop in the question.
        for v := range valueChannel {
            fmt.Println(v)
        }
     }
    

    Run the example on the playground.

    上面的代码独立于 goroutines 发送的值的数量。 如果 main() 函数可以确定 goroutines 发送的值的数量,则从 main() 接收该数量的值:

    func main() {
        const n = 10
    
        valueChannel := make(chan int, 2)
        for i := 0; i < n; i++ {
            go doNothing(valueChannel)
        }
    
        // Each call to doNothing sends one value. Receive
        // one value for each call to doNothing.
        for i := 0; i < n; i++ {
            fmt.Println(<-valueChannel)
        }
    }
    
    func doNothing(numChan chan int) {
        time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
        numChan <- 12
    }
    

    Run the example on the playground.

    【讨论】:

    • 非常感谢。以上工作。但是,为什么 ``` wg.Wait() 和 close(valueChannel) ``` 应​​该在 go 例程中?我试图将它们放在 go 例程之外,但失败了 :(
    • 要中断接收循环,对close(valueChannel) 的调用必须与接收循环同时执行。 Goroutines 用于并发执行代码。
    • main函数作为Goroutine运行,你可以直接把close放在main里面。 @CeriseLimón
    • @CeriseLimón,那为什么我的代码对我的答案有效?
    • 第3行有:defer close(valueChannel)
    【解决方案2】:

    主要问题在频道接收的for循环上。 逗号 ok 习语在通道上略有不同,ok 表示接收到的值是在通道上发送的(真)还是因为通道关闭且为空而返回的零值(假)。 在这种情况下,通道正在等待发送数据,因为它已经完成了十次发送值:死锁。 因此,除了代码设计之外,如果我只需要在此处进行尽可能少的更改,那就是:

    func main() {
        valueChannel := make(chan int, 2)
        defer close(valueChannel)
        var wg sync.WaitGroup
        for i := 0; i < 10; i++ {
            wg.Add(1)
            go doNothing(&wg, valueChannel)
        }
    
        for i := 0; i < 10; i++ {
            v := <- valueChannel
    
            fmt.Println(v)
        }
        wg.Wait()
    }
    
    func doNothing(wg *sync.WaitGroup, numChan chan int) {
        defer wg.Done()
        time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
        numChan <- 12
    }
    

    【讨论】:

      猜你喜欢
      • 2017-09-06
      • 1970-01-01
      • 1970-01-01
      • 2021-08-15
      • 2017-06-24
      • 2012-10-17
      • 1970-01-01
      • 2018-02-02
      • 1970-01-01
      相关资源
      最近更新 更多