【问题标题】:Channel Deadlock in GoGo中的通道死锁
【发布时间】:2017-06-24 10:06:30
【问题描述】:

我收到“致命错误:所有 goroutine 都在休眠 - 死锁! “出于某种原因,在下面的代码中。我正在使用应该是非阻塞的缓冲通道。不知道我做错了什么

package main

import (
    "fmt"
    "sync"
)

func main() {
    c := make(chan int, 2)
    var wg sync.WaitGroup
    wg.Add(2)

    go doSomething(c, wg)
    go doSomething(c, wg)
    go doSomething(c, wg)

    wg.Wait()

    close(c)

    for v := range c {
        fmt.Print(v)
    }

}

func doSomething(c chan<- int, wg sync.WaitGroup) {
    defer wg.Done()
    c <- 1

}

游乐场链接https://play.golang.org/p/J9meD5aKna

【问题讨论】:

    标签: go


    【解决方案1】:

    虽然您的解决方案可能有效,但我对此并不满意。

    首先,您需要更改通道大小以使其工作这一事实表明存在潜在的问题/错误。现在,每次您想启动另一个doSomething 时,您都必须记住更改频道的长度。

    其次,你要等到所有的 goroutine 都完成后再从 channel 中读取。这是一种“浪费”,因为在通道上循环范围的要点之一是您不必等到所有项目都生成(写入通道),您可以立即开始处理项目其中已经准备好了。

    所以我会把你的代码写成类似

    func main() {
        c := make(chan int)
    
        var wg sync.WaitGroup
        wg.Add(3)
        go func() {
            doSomething(c)
            defer wg.Done()
        }()
        go func() {
            doSomething(c)
            defer wg.Done()
        }()
        go func() {
            doSomething(c)
            defer wg.Done()
        }()
    
        go func() {
            wg.Wait()
            defer close(c)
        }()
    
        for v := range c {
            fmt.Print(v)
        }
    }
    
    func doSomething(c chan<- int) {
        c <- 1
    }
    

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

    注意等待和关闭通道现在是如何在它自己的 goroutine 中进行的 - 这允许立即开始迭代通道(现在没有缓冲!)。

    我还更改了代码,使WaitGroup 永远不会离开它被声明的范围(即它不用作参数),这是我个人的偏好。我相信它使代码更容易理解和理解。

    【讨论】:

    • 我认为在频道关闭之前你不能跨越频道。也许我错了,但这就是guzalexander.com/2013/12/06/golang-channels-tutorial.html这里所说的“正如上面提到的,范围将一直有效,直到通道被明确关闭”。在这种情况下,我们需要等待另一个 goroutine 执行完毕。
    • 你误解了——关键是在通道关闭之前循环不会结束。因此,如果通道永远不会关闭,您将陷入无限循环(它会等待下一个项目或通道关闭)。
    • 哦,好吧,所以你的意思是 for 范围将阻塞直到通道关闭。我认为你的解决方案更好
    【解决方案2】:

    是的,您的代码中有一个重要的问题

    您调用go doSomething(c, wg) 只需传递wg 值。 你应该知道

    Go 中的参数总是按值传递。使用指针时 参数可能会被修改。

    所以你应该这样做go doSomething(c, &amp;wg),然后main函数中的wg将被func doSomething(c chan&lt;- int, wg sync.WaitGroup)中的defer wg.Done()修改。

    【讨论】:

      【解决方案3】:

      我发现了问题。其实有两个问题

      1. channel和wg的大小应该是3

      2. 我应该将 wg 作为指针传递

      更新代码

      package main
      
      import (
          "fmt"
          "sync"
      )
      
      func main() {
          c := make(chan int, 3)
          var wg sync.WaitGroup
          wg.Add(3)
      
          go doSomething(c, &wg)
          go doSomething(c, &wg)
          go doSomething(c, &wg)
      
          wg.Wait()
      
          close(c)
      
          for v := range c {
              fmt.Print(v)
          }
      
      }
      
      func doSomething(c chan<- int, wg *sync.WaitGroup) {
          defer wg.Done()
          c <- 1
      
      }
      

      【讨论】:

        猜你喜欢
        • 2018-02-02
        • 1970-01-01
        • 2021-12-25
        • 2020-01-24
        • 1970-01-01
        • 2017-09-06
        • 1970-01-01
        • 1970-01-01
        • 2017-10-05
        相关资源
        最近更新 更多