当前位置: 首页 > 面试题库 >

解决goroutines死锁

澹台景辉
2023-03-14
问题内容

我一直在尝试解决我在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

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


问题答案:

您需要在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

希望能帮助到你。



 类似资料:
  • 问题内容: 我试图了解Go中的并发性。特别是,我编写了以下线程不安全程序: 我认识到我应该使用渠道来防止与发生竞争,但这不是重点。程序打印,然后似乎永远循环(不再打印任何内容)。我希望它能打印出无限的数字列表,可能由于竞态条件而跳过某些数字并重复其他数字(或更糟的是,在更新数字时打印数字)。 我的问题是:为什么程序只打印一行? 只是要清楚一点:对于这个玩具示例,我不是故意使用渠道。 问题答案: 关

  • 本文向大家介绍Java  解决死锁的方法实例详解,包括了Java  解决死锁的方法实例详解的使用技巧和注意事项,需要的朋友参考一下 死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。 java 死锁产生的四个必要条件: 1>互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用 2>不可抢占,资源请求者不能强制

  • 8.1. Goroutines 在Go语言中,每一个并发的执行单元叫作一个goroutine。设想这里的一个程序有两个函数,一个函数做计算,另一个输出结果,假设两个函数没有相互之间的调用关系。一个线性的程序会先调用其中的一个函数,然后再调用另一个。如果程序中包含多个goroutine,对两个函数的调用则可能发生在同一时刻。马上就会看到这样的一个程序。 如果你使用过操作系统或者其它语言提供的线程,那

  • 本文向大家介绍详解vue beforeEach 死循环问题解决方法,包括了详解vue beforeEach 死循环问题解决方法的使用技巧和注意事项,需要的朋友参考一下 什么是beforeEach? beforeEach 是一个vue-router的路由导航钩子,一般我用它做路由守卫。 什么是路由守卫? 路由跳转前做一些验证,比如登录验证,是网站中的普遍需求。对此,vue-route 提供的befo

  • 8.7. Goroutines Go语言中使用go可以启动一个goroutine。goroutine 和线程的概念类似,和程序共享一个地址空间。 goroutines和支持多路并发草组系统中的协程(coroutines)类似,用户不用关心具体 的实现细节。 func server(i int) { for { print(i) sys.sle