Go中的频道死锁

时间:2017-02-07 08:38:32

标签: go

我得到了“致命的错误:所有的goroutines都睡着了 - 僵局! “由于某些原因,在下面的代码中。我使用缓冲通道,应该是非阻塞的。不确定我做错了什么

/**
 * Returns a HashSet of the string permutation.
 *
 * @param prefix an empty string.
 * @param str the string to create perm.
 * @return permutations a HashSet of the permutation.
 */
private static HashSet<String> permutation(String prefix, String str) {
    HashSet<String> permutations = new HashSet<String>();
    int n = str.length();
    if (n == 0) {
        permutations.add(prefix);
    } else {
        for (int i = 0; i < n; i++) {
            permutations.addAll(permutation(prefix + str.charAt(i), str.substring(0, i) + str.substring(i + 1, n)));
        }
    }
    return permutations;
}

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

3 个答案:

答案 0 :(得分:1)

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

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

其次,你要等到所有goroutine完成才能从频道中读取。这是一种浪费&#34;作为通道上的范围循环的一个要点是,您不必等到生成所有项目(写入通道),您可以在其中一些项目准备好后立即开始处理这些项目。

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

的代码
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永远不会离开声明它的范围(即它不被用作参数),这是我个人的偏好。我相信它使代码更容易理解。

答案 1 :(得分:0)

我发现了问题。实际上有两个问题

  1. 频道和wg的大小应为3

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

  3. 更新了代码

    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
    
    }
    

答案 2 :(得分:0)

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

您致电go doSomething(c, wg)只需传递wg值即可。 你应该知道

  

Go中的参数始终按值传递。当一个时使用指针   参数可以修改。

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