This question already has an answer here:
I am trying to have two separate consumer go routines, that would filter out even and odd numbers from the input channel. This is just a toy example in order to see if it is possible to have consumers do something with the message read from the input channel if it matches certain condition, otherwise put back onto the input channel.
My current code is as follows:
package main
func filterOdd(ch chan int, out chan int) {
val := <- ch
if val % 2 == 0 {
ch <- val
} else {
out <- val
}
}
func filterEven(ch chan int, out chan int) {
val := <- ch
if val % 2 != 0 {
ch <- val
} else {
out <- val
}
}
func main() {
even := make(chan int)
odd := make(chan int)
input := make(chan int)
go filterOdd(input, odd)
go filterEven(input, even)
for i:=1; i <= 10; i++ {
input <- i
}
println("Even...")
for i := range even {
println(i)
}
println("Odd...")
for i := range odd {
println(i)
}
}
However, this produces the following output:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/tmp/sandbox594577124/main.go:27 +0x140
goroutine 4 [chan send]:
main.filterOdd(0x10336100, 0x103360c0)
/tmp/sandbox594577124/main.go:8 +0xc0
created by main.main
/tmp/sandbox594577124/main.go:24 +0xc0
Link to the Go Playground: https://play.golang.org/p/9RIvFsGKI-
</div>
You have a deadlock because your even and odd goroutines are blocked on sending to out
because nothing is reading from it. Why is nothing reading out
? Because the main
goroutine is blocked on sending to input
because nothing is reading from it. Why is nothing reading from input
? Because the two goroutines that would read from it are blocked.
Also, both filterEven
and filterOdd
will only run once unless you wrap their contents in for { }
(but then they'll never stop until you break
). On the other hand, range even
will block (and range odd
never happens) when nothing is left to write to even
, because range
over a channel only stops when the channel is closed or break
is called.
In general, these aren't hard problems to solve as long as you know when you can close a channel. With what you're describing, that gets more difficult. None of the goroutines know when it's OK to close input
, because all three write to it and two also read from it. You can use a sync.WaitGroup
to make sure that everything you've put into the input
channel has been processed before closing it. Once it's closed, the two other goroutines can use that as a signal to close their own channels and break
or return
to finish running.
However, writes to the in
and out
channels will still block until there is a corresponding read, because they are unbuffered. However, if you buffer them by specifying a size as the second argument to make
, writes won't block until the channel is full. Since you know neither even
or odd
will have more written to them than what main
send to input
, you can use that as a safe buffer capacity.
Here's an example of using a WaitGroup
with buffered channels for your code: https://play.golang.org/p/VXqfwUwRcx
If you don't want buffered channels, you can also use another pair of goroutines to capture the values and send them back to main
as slices once finished. This way writes on the even
and odd
channels don't block: https://play.golang.org/p/i5vLDcsK1v
Otherwise, if don't need to print the contents of each channel all at once, you can use those two extra goroutines to read from the channels and print right away: https://play.golang.org/p/OCaUTcJkKB