小编典典

解决goroutines死锁

go

我一直在尝试解决我在Golang并发中遇到的这个简单问题。我一直在搜索所有可能的解决方案,但没有发现与我的问题有关的特定信息(否则我可能会被遗漏)。这是我的代码:

package main

import (
    "fmt"
    "time"
)

func producer(ch chan int, d time.Duration, num int) {

    for i:=0; i<num; i++ {
        ch <- i
        time.Sleep(d)
    }
}

func main() {
    ch := make(chan int)

    go producer(ch, 100*time.Millisecond, 2)
    go producer(ch, 200*time.Millisecond, 5)

    for {
        fmt.Println(<-ch)    
    }

    close(ch)
}

它显示错误:

致命错误:所有goroutine都在睡觉-死锁!

goroutine 1 [chan接收]:main.main()D:/Code/go/src/testconcurrency/main.go:23 +
0xca退出状态2

避免此错误的有效方法是什么?,谢谢。


阅读 252

收藏
2020-07-02

共1个答案

小编典典

您需要在goroutine中同步所有异步过程。您的主线程和goroutine线程不是同步进程。您的主线程永远不会知道何时停止从goroutines调用通道。因为您的主线程在通道上循环,所以它总是从通道中调用值,并且当goroutine完成并且通道停止发送值时,您的主线程无法再从通道中获取值,因此条件变为死锁。为了避免这种使用sync.WaitGroup来同步异步过程。

这是代码:

package main

import (
    "fmt"
    "time"
    "sync"
)

func producer(ch chan int, d time.Duration, num int, wg *sync.WaitGroup) {
    for i:=0; i<num; i++ {
        ch <- i;
        time.Sleep(d);
    }
    defer wg.Done();
}

func main() {
    wg  := &sync.WaitGroup{}
    ch  := make(chan int);

    wg.Add(2);
    go producer(ch, 100*time.Millisecond, 2, wg);
    go producer(ch, 200*time.Millisecond, 5, wg);

    go func() {   
    wg.Wait()
    close(ch)
    }()

    // print the outputs
    for i:= range ch {
        fmt.Println(i);
    }
}

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

希望能帮助到你。

由于我的解决方案看起来与已回答的问题有点相似,因此我将其更改为我的原始回答,然后进行修改以适应OP问题。

这是代码:

package main

import (
    "fmt"
    "time"
    "sync"
)

// producer produce values tobe sent to consumer
func producer(ch chan int, d time.Duration, num int, wg *sync.WaitGroup) {
    defer wg.Done();
    for i:=0; i<num; i++ {
        ch <- i;
        time.Sleep(d);
    }
}

// consumer consume all values from producers
func consumer(ch chan int, out chan int, wg *sync.WaitGroup) {
    defer wg.Done();
    for i:= range ch {
        out <- i
    }
}

// synchronizer synchronize all goroutines to avoid deadlocks
func synchronizer(ch chan int, out chan int, wgp *sync.WaitGroup, wgc *sync.WaitGroup) {
    wgp.Wait()
    close(ch)
    wgc.Wait()
    close(out)
}

func main() {
    wgp  := &sync.WaitGroup{}
    wgc  := &sync.WaitGroup{}
    ch  := make(chan int);
    out := make(chan int);

    wgp.Add(2);
    go producer(ch, 100*time.Millisecond, 2, wgp);
    go producer(ch, 200*time.Millisecond, 5, wgp);

    wgc.Add(1);
    go consumer(ch, out, wgc)

    go synchronizer(ch, out, wgp, wgc)

    // print the outputs
    for i:= range out {
        fmt.Println(i);
    }
}

consumergoroutine用于fan-in来自多个goroutine的所有输入,并从goroutine读取所有值consumer

希望能帮助到你。

2020-07-02