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

所有戈鲁丁都睡着了 - 死锁问题

所有戈鲁丁都睡着了 - 死锁问题

Go
慕莱坞森 2022-08-01 10:44:50
我试图在同步管理器(1,2,3,4...)中打印奇数,使用两个goroutine,我很困惑为什么我的代码会导致死锁。请你能帮我理解吗?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.(*等待组)。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贡献1863条经验 获得超2个赞

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

ch := make(chan bool, 1)

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

关于打印的顺序:没有顺序,其中go例程将首先开始,go lang规范没有提到多个go例程是否从一个通道接收数据,那么首先等待的人是否真的首先获得它。因此,如果需要,您需要添加其他同步以维护顺序。以下是订购同步的提示

下面是打印的修改代码,但我更喜欢不同的方法。检查上面链接中的乒乓球示例,其中使用了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-08-01
?
犯罪嫌疑人X

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

首先,您的两个 Goroutine 需要从通道接收才能开始工作ch


for i<=10{

        <-ch // wait until receive

        fmt.Println(i)

        i+=2

        ch <- true

    }

因此,您将值发送到通道,以使两个 goroutine 工作ch


func main() {

    //...

    ch <- true

    //...

}

但这不会像预期的那样工作,因为你们的两个goroutine共享同一个频道。执行时,只有一个可以接收、开始工作并将值发送回通道。chch <- truemain()ch


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


换句话说,两个 goroutine 通过使用通道相互发送和接收值ch


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仍然试图在下班后发送到通道,但没有接收器,这会导致死锁ch


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

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信