【问题标题】:why is this a deadlock in golang / waitgroup?为什么这是 golang / waitgroup 中的死锁?
【发布时间】:2014-08-27 20:45:22
【问题描述】:

我不确定我错过了什么,但我遇到了死锁错误。在所有 go 例程完成后,我正在使用一个缓冲通道。该通道的容量为 4,我正在运行 4 个 go 例程,因此我希望它在达到最大容量后自动“关闭”。

package main

import "fmt"
import "sync"

func main() {
    ch := make(chan []int, 4)
    var m []int

    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            ch <- m
            return
        }()
    }
    wg.Wait()

    for c := range ch {
        fmt.Printf("c is %v", c)
    }
}

【问题讨论】:

  • 你在哪里填充m
  • 问题是 range ch 仍在等待元素进入通道,并且没有 goroutine 可以写。
  • 我假设我正在发送一个空/归零切片。即使我在ch &lt;- m 之前添加m = append(m, i),它仍然会陷入僵局。见play.golang.org/p/uAOnq6SdeR
  • rangeing 如果您缺少的频道关闭,则通过频道工作。

标签: go


【解决方案1】:

你有两个问题:

  • 由于您的频道太小,所有 goroutine 都没有足够的空间:当您的频道已满时,剩余的 goroutine 必须等待一个插槽被释放
  • range ch 仍在等待元素进入通道,并且没有 goroutine 可写。

解决方案 1:

使通道足够大,然后关闭它,以便range 停止等待:

ch := make(chan []int, 5)
...
wg.Wait()
close(ch)

Demonstration

这可行,但这主要违背了此处通道的目的,因为您不会在所有任务完成之前开始打印。

解决方案 2: 该解决方案将允许真正的流水线(即较小的通道缓冲区),将在打印时执行Done()

func main() {
    ch := make(chan []int, 4)
    var m []int

    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func() {
            ch <- m
            return
        }()
    }
    go func() {
        for c := range ch {
            fmt.Printf("c is %v\n", c)
            wg.Done()
        }
    }()
    wg.Wait()
}

Demonstration

【讨论】:

  • 如果通道耗尽,为什么范围 ch 仍在等待元素?我假设一旦我发送第 4 个值,范围就会到达“循环”的末尾,因此它不应再等待值。
  • range 等到通道关闭。这就是它的工作原理。这样,您可以启动一个 goroutine,并且如果您稍后将元素推送到通道中,它仍然可以工作。
  • 感谢您的回答,尽管这很奇怪,因为我有一个应用程序可以做同样的事情,只是它不会陷入僵局。它遍历一些 sql 行,启动 go 例程,我在 for 循环之后 wg.Wait(),在通道上进行范围并显示每个例程的结果。没有“close(ch)”操作。我只能假设程序本身陷入僵局,但我没有收到错误。
  • 关于第二个解决方案,我不确定我是否理解,但它仍然会陷入僵局,除非main 中没有抛出错误?
  • 对不起...我想我已经知道它是如何工作的了。无论如何 wg.Done() 不应该在 fmt.Printf("c is %v\n", c) play.golang.org/p/Be8eEc6PMu 之后
【解决方案2】:

我正在运行 4 个 go 例程

不,您正在运行 5 - 因此是死锁,因为通道仅缓冲 4 条消息。

但是,在通道上进行迭代也会在稍后使程序死锁。由于通道没有关闭,一旦读取了您的 5 个值,它将永远阻塞。

所以,

ch := make(chan []int, 5)

close(ch) 

range 循环之前。

【讨论】:

    【解决方案3】:

    你可以尝试运行这个没有 wg 的代码,但仍然死锁; 为什么? 只是因为主 goroutine 被 ch 阻塞了,所以程序被挂起和 timeout ,你会得到一个死锁错误。 根本原因是 ch 阻塞了主 goroutine。

    func main() {
        ch := make(chan []int, 4) // No matter which int number 
        var m []int
        for i := 0; i < 5; i++ {
            go func() {
                ch <- m
                return
            }()
        }
        for c := range ch {
            fmt.Printf("c is %v \n", c)
        }
    }
    

    【讨论】:

    • 我也有类似的情况,请问如何解决?
    【解决方案4】:

    @Denys Séguret 是对的

    但以下是更容易修复它的另一种解决方案:

    func main() {
        ch := make(chan []int, 4)
        var m []int
    
        var wg sync.WaitGroup
        for i := 0; i < 5; i++ {
            wg.Add(1)
            go func() {
                defer wg.Done()
                ch <- m
                return
            }()
        }
        go func() {
            wg.Wait()
            close(ch)
        }()
        for c := range ch {
            fmt.Printf("c is %v", c)
        }
    }
    

    form https://go.dev/blog/pipelinesfan-out, fan-in 的 func merge()

    func merge(cs ...<-chan int) <-chan int {
        var wg sync.WaitGroup
        out := make(chan int)
    
        // Start an output goroutine for each input channel in cs.  output
        // copies values from c to out until c is closed, then calls wg.Done.
        output := func(c <-chan int) {
            for n := range c {
                out <- n
            }
            wg.Done()
        }
        wg.Add(len(cs))
        for _, c := range cs {
            go output(c)
        }
    
        // Start a goroutine to close out once all the output goroutines are
        // done.  This must start after the wg.Add call.
        go func() {
            wg.Wait()
            close(out)
        }()
        return out
    }
    

    【讨论】:

      猜你喜欢
      • 2021-06-20
      • 2021-12-10
      • 1970-01-01
      • 1970-01-01
      • 2019-12-27
      • 2010-09-07
      • 2011-04-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多