以下 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>
提前致谢
我想说
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
,这将使其关闭生成的通道并退出。