为了账号安全,请及时绑定邮箱和手机立即绑定

所有 goroutine 都处于休眠状态 - 死锁问题

所有 goroutine 都处于休眠状态 - 死锁问题

Go
慕姐8265434 2022-07-25 10:59:49
我正在尝试使用两个 goroutine 在同步管理器(1、2、3、4 ...)中打印奇偶数,我很困惑为什么我的代码会导致死锁。请你能帮我理解吗?package mainimport (    "fmt"    "sync")var wg sync.WaitGroupfunc odd(ch chan bool){    i :=1    for i<=10{        <-ch        fmt.Println(i)        i+=2        ch<-true    }    wg.Done()}func even(ch chan bool){    i :=2    for i<=10{        <-ch        fmt.Println(i)        i+=2        ch <- true    }    wg.Done()}func main() {    ch :=make(chan bool)    wg.Add(2)    go even(ch)    go odd(ch)    ch <- true    wg.Wait()}O/P: 1 2 3 4 5 6 7 8 9 10 致命错误:所有 goroutine 都处于休眠状态 - 死锁!goroutine 1 [semacquire]: sync.runtime_Semacquire(0x5844a8) /usr/local/go-faketime/src/runtime/sema.go:56 +0x45 sync.(*WaitGroup).Wait(0x5844a0) /usr/local/go- faketime/src/sync/waitgroup.go:130 +0x65 main.main() /tmp/sandbox505861393/prog.go:37 +0xcfgoroutine 6 [chan send]: main.even(0xc00005e060) /tmp/sandbox505861393/prog.go:26 +0xc5 由 main.main 创建 /tmp/sandbox505861393/prog.go:34 +0x7f当我更改 goroutine 的顺序时,o/p 开始以奇偶方式打印,我也很难理解这一点。我会很感激你的帮助,谢谢。
查看完整描述

2 回答

?
芜湖不芜

TA贡献1796条经验 获得超7个赞

首先,你的两个 goroutine 需要从chchannel 接收才能开始工作


for i<=10{

        <-ch // wait until receive

        fmt.Println(i)

        i+=2

        ch <- true

    }

因此,您向通道发送一个值以ch使您的两个 goroutine 工作


func main() {

    //...

    ch <- true

    //...

}

但这不会按预期工作,因为您的两个 goroutine 共享同一个ch通道。在执行时,它们ch <- true中main()只有一个可以接收、开始工作并将值发送回ch通道。


之后,两个 goroutine 不断从ch通道接收,开始工作并将值发送回通道


换句话说,两个 goroutine 使用chchannel相互发送和接收值


for i<=10 {

    <-ch           // receive when main() executed at first time, after that receive from another goroutine

    fmt.Println(i) // start work

    i+=2           //

    ch <- true     // return back to the channel, another goroutine will receive it

}

wg.Done()

但是问题是当一个goroutine退出时,剩下的goroutine在工作后仍然尝试发送到chchannel,但是没有receiver,这会导致死锁


查看完整回答
反对 回复 2022-07-25
?
繁花不似锦

TA贡献1851条经验 获得超4个赞

如果通道中没有空间,在通道上发送也会阻塞。如果您在通道中使用缓冲区,则发送可以工作。仅使用 1:


ch := make(chan bool, 1)

现在您可以发送数据了,它不会因为未满而阻塞 go 例程。如果您再次发送而不读取,那么它将阻止发送调用,因为再次没有空间并且之前的值仍然没有被消耗。


关于打印的顺序: goroutine 将首先开始并没有顺序,go lang 规范没有提到多个 goroutine 是否正在从一个通道接收数据,那么首先等待的是否真的首先得到它。因此,如果需要,您需要添加额外的同步来维护订单。这是订购同步的提示。


下面是修改后的打印代码,但我更喜欢不同的方法。检查上面链接中的 ping - pong 示例,其中使用了 2 个单独的通道而不是一个。


package main


import (

    "fmt"

    "sync"

)


func odd(wg *sync.WaitGroup, ch chan bool) {

    defer wg.Done()

    i := 1

    for i <= 10 {

        <-ch

        fmt.Println(i)

        i += 2

        ch <- true

    }

}


func even(wg *sync.WaitGroup, ch chan bool) {

    defer wg.Done()

    i := 2

    for i <= 10 {

        <-ch

        fmt.Println(i)

        i += 2

        ch <- true

    }


}


func main() {

    var wg sync.WaitGroup

    ch := make(chan bool, 1)

    defer close(ch)

    wg.Add(2)

    go even(&wg, ch)

    go odd(&wg, ch)

    ch <- true

    wg.Wait()


}


查看完整回答
反对 回复 2022-07-25
  • 2 回答
  • 0 关注
  • 81 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号