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)
}
This was an example of bad approach in fanin pattern for concurrency in Go. There happens deadlocks when ranging fanin in the end of yielding all the values and in first case of select:
...
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
But when I made an assignment first instead of simultaneous sending, It started to work without deadlock:
// 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)
}
Just can't understand the reasons of this behaviour.
A select
statement evaluates the arguments of a send or receive operation. When you write
case fanin <- <-even:
it first evaluates <-even
, and then checks if fanin
is ready to receive. If even
is not ready, this will block.
In short, a select
statement selects on one channel operation for each case. You are trying to do two.