package main
import (
"fmt"
)
func main() {
even := make(chan int)
odd := make(chan int)
quit := make(chan int)
fanin := make(chan int)
go send(even, odd, quit)
go receive(even, odd, quit, fanin)
for v := range fanin {
fmt.Println(v)
}
fmt.Println("about to exit")
}
// send channel
func send(even, odd, quit chan<- int) {
for i := 0; i < 100; i++ {
if i%2 == 0 {
even <- i
} else {
odd <- i
}
}
close(quit)
}
// receive channel
func receive(even, odd, quit <-chan int, fanin chan<- int) {
thisLoop:
for {
select {
case fanin <- <-even: //problem is here
case fanin <- <-odd:
case <-quit:
break thisLoop
}
}
close(fanin)
}
这是 Go 中并发扇入模式的糟糕方法的一个例子。在产生所有值的最后和第一种选择的情况下,当范围扇入时会发生死锁:
...
95
96
99
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/home/floosde/go/src/floosde/main.go:17 +0x194
goroutine 19 [chan receive]:
main.receive(...)
/home/floosde/go/src/floosde/main.go:41
created by main.main in goroutine 1
/home/floosde/go/src/floosde/main.go:15 +0x137
exit status 2
但是当我先进行分配而不是同时发送时,它开始工作而没有死锁:
// receive channel
func receive(even, odd, quit <-chan int, fanin chan<- int) {
thisLoop:
for {
select {
case v := <-even: // Fixed version
fanin <- v
case v := <-odd: // Fixed version
fanin <- v
case <-quit:
break thisLoop
}
}
close(fanin)
}
只是无法理解这种行为的原因。
A
select
语句评估发送或接收操作的参数。当你写的时候
case fanin <- <-even:
它首先评估
<-even
,然后检查fanin
是否准备好接收。如果 even
未准备好发送,则会阻塞。
简而言之,
select
语句为每种情况选择一个通道操作。你正在尝试做两个。