一个通道带有一个接收器,未知数量的goroutine发送器导致死锁

时间:2019-01-13 05:22:58

标签: go deadlock channel goroutine

我有一个频道,接收器是主频道。我产生了多个goroutine,每个goroutine通过通道发送一个字符串。

现在,这会导致死锁,因为我没有使用关闭功能正确关闭通道。事实是,我不知道会创建多少个goroutine,所以无法知道何时关闭通道。

我尝试使用WaitGroup,问题是,我读到我不能在goroutine中使用Add,而应该在主进程/ goroutine中使用wg.Add(1),我已经尝试过在父goroutine中使用Add生成子goroutine,这也会导致死锁

软件包主要

import (
    "fmt"
    "sync"
)

var i = 0

func doSomething(ch chan string, wg sync.WaitGroup) {
    defer wg.Done()
    ch <- fmt.Sprintf("doSomething: %d", i)
    i++
    if i == 10 {return}
    wg.Add(1)
    go doSomething(ch, wg)
}

func main() {
    ch := make(chan string)
    var wg sync.WaitGroup
    wg.Add(1)
    go doSomething(ch, wg)
    wg.Wait()
    for s := range ch {
        fmt.Println(s)
    }
}

现在,这只是一个测试代码,因此,假设我们不知道我们将仅创建10个goroutine,假设它在运行时未知,那么如果我不这样做,我会立即得到一个死锁错误而没有任何输出'不使用WorkGroup,在打印第10个字符串之前会收到错误消息(因为我没有关闭通道)

我也尝试过不为每个函数调用生成一个goroutine,而是对所有递归调用使用一个goroutine(从main开始),并关闭通道,我为go创建了一个匿名函数,该函数首先调用doSomething函数然后调用close,因此所有递归调用都将得到评估,我们将确定何时关闭通道。但是,这就是我现在要完成的工作,我试图让未知数量的goroutine一起工作,并以某种方式完成后关闭通道。

1 个答案:

答案 0 :(得分:3)

有几个问题。

第一个是程序在将等待组值作为参数传递时会复制它们。复制后,等待组无法正常工作。而是将指针传递给等待组。

第二个问题是main在从通道接收值之前等待所有goroutine完成。由于通道的缓冲区不足以容纳所有发送的值,因此程序会死锁。

第三个问题是通道的主要范围,但没有任何通道关闭通道。 Main不会因此退出。

要解决第二和第三个问题,请启动另一个goroutine以等待doSomthing并在完成后关闭频道。

尝试一下:

func doSomething(ch chan string, wg *sync.WaitGroup) {
    defer wg.Done()
    ch <- fmt.Sprintf("doSomething: %d", i)
    i++
    if i == 10 {
        return
    }
    wg.Add(1)
    go doSomething(ch, wg)
}

func main() {
    ch := make(chan string)
    var wg sync.WaitGroup
    wg.Add(1)
    go doSomething(ch, &wg)
    go func() {
        wg.Wait()
        close(ch)
    }()
    for s := range ch {
        fmt.Println(s)
    }
}