Goroutine 死锁

问题描述 投票:0回答:1

以下 goroutine 代码返回死锁...

请帮我找出问题所在 包主

import "fmt"

func main() {
    // create & consume channel
    counter := make(chan int)
    even := make(chan int)
    odd := make(chan int)
    square := make(chan int)
    merge := make(chan int)
    out := make(chan struct{})

    //GoRoutines
    go counterFn(counter)
    go squarerFn(counter, square)
    go counterSplit(counter, even, odd)
    go merger(square, odd, merge)
    go printOut(merge, out)
    <-out
}

func counterFn(counter chan int) {
    for i := 0; i < 5; i++ {
        counter <- i
    }
    close(counter)
}

func squarerFn(counter chan int, square chan int) {
    for i := range counter {
        square <- i * i
    }
    close(square)
}

func counterSplit(counter chan int, even chan int, odd chan int) {
    for i := range counter {
        if i%2 == 0 {
            even <- i
        } else {
            odd <- i
        }
    }
    close(even)
    close(odd)
}

func merger(even chan int, odd chan int, merge chan int) {
    i := 0
    for {
        fmt.Printf("%d \n", i)
        a, ok := <-even
        if !ok {
            i++
        } else {
            merge <- a
        }
        a, ok = <-odd
        if !ok {
            i++
        } else {
            merge <- a
        }
        if i == 2 {
            break
        }
    }
    close(merge)
}

func printOut(merge chan int, out chan struct{}) {
    for i := range merge {
        fmt.Print(i)
    }
    close(out)
}

这是输出

go run channel_pipelineAdv.go
0 , 0
1
0 , 9
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        D:/channel_pipelineAdv.go:20 +0x233

goroutine 7 [chan send]:
main.squarerFn(0x0?, 0x0?)
        D:/channel_pipelineAdv.go:32 +0x45
created by main.main in goroutine 1
        D:/channel_pipelineAdv.go:16 +0x114

goroutine 8 [chan send]:
main.counterSplit(0x0?, 0x0?, 0x0?)
        D:/channel_pipelineAdv.go:40 +0x5e
created by main.main in goroutine 1
        D:/channel_pipelineAdv.go:17 +0x179

goroutine 9 [chan receive]:
main.merger(0x0?, 0x0?, 0x0?)
        D:/channel_pipelineAdv.go:59 +0xca
created by main.main in goroutine 1
        D:/channel_pipelineAdv.go:18 +0x1d9

goroutine 10 [chan receive]:
main.printOut(0x0?, 0x0?)
        D:/channel_pipelineAdv.go:73 +0x77
created by main.main in goroutine 1
        D:/channel_pipelineAdv.go:19 +0x227
exit status 2
PS D:\Study\Go\GoLearn\basic> 

提前致谢

go goroutine
1个回答
0
投票

我想说

merger
的代码是有问题的:它依赖于将
i
递增到2的限制似乎是基于
even
odd
大致相同地关闭的期望时间,然后从
even
读取返回
false
作为其第二个值,然后再从
odd
读取 - 也返回
false
作为其第二个值。

这种期望是没有根据的:任何 goroutine 都可能因为某种原因而“停滞”,这会让其他 goroutine 取得“更多”进展。

想一想,如果发送到

odd
的 Goroutine 获得的计算量比发送到
even
的计算量少得多,会发生什么。 在这种情况下,一旦
even
关闭,而
odd
未关闭,
merger
中的循环将愉快地执行来自
even
的接收,每次在关闭的 even 上执行时,都会成功
 发生 –每次都返回 
false
,最终将 
i
 升至 2。

我们可以通过使用在循环中的多个通道上运行的

switch

 语句与“我们完成了”的通道相结合来修复您的代码:
disabling

游乐场
。)

请注意,一旦我们完成了package main import "fmt" func main() { // create & consume channel counter := make(chan int) even := make(chan int) odd := make(chan int) square := make(chan int) merge := make(chan int) out := make(chan struct{}) //GoRoutines go counterFn(counter) go squarerFn(counter, square) go counterSplit(counter, even, odd) go merger(square, odd, merge) go printOut(merge, out) <-out } func counterFn(counter chan int) { for i := 0; i < 5; i++ { counter <- i } close(counter) } func squarerFn(counter chan int, square chan int) { for i := range counter { square <- i * i } close(square) } func counterSplit(counter chan int, even chan int, odd chan int) { for i := range counter { if i%2 == 0 { even <- i } else { odd <- i } } close(even) close(odd) } func merger(even chan int, odd chan int, merge chan int) { for { select { case a, ok := <-even: if ok { merge <- a } else { even = nil } case b, ok := <-odd: if ok { merge <- b } else { odd = nil } default: close(merge) return } } } func printOut(merge chan int, out chan struct{}) { for i := range merge { fmt.Print(i) } close(out) }

,我们就会用

even

覆盖它,这将使下一次尝试从中读取
无限期地挂起
,实际上使
nil总是更喜欢另一个分支——如果有的话。最终以同样的方式禁用 select
 (现在,你应该明白 
odd
 可能会比 
odd
 更早关闭,我只是继续解释的第一部分)将使 
even
 更喜欢只剩下一个分支,
select
,这将使其关闭生成的通道并退出。

© www.soinside.com 2019 - 2024. All rights reserved.