I try to multiplex 2 channels, A and B. A is sending with 10 milliseconds delay and B is 1 second. I use select to wait for A and B, and send the result to a fan-in channel, and then receive value in main.
package main
import (
"fmt"
"time"
)
func talk(msg string, wait_time int) <-chan string {
ch := make(chan string)
go func () {
for i:=0;i<5;i++ {
ch <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(wait_time)*time.Millisecond)
}
}()
return ch
}
func fanIn(input1, input2 <-chan string) <-chan string {
ch := make(chan string)
go func () {
for {
select {
case t :=<-input1:
ch <- t
case t := <-input2:
ch <- t
}
}
}()
return ch
}
func main() {
ch := fanIn(talk("A", 10), talk("B", 1000))
for i:=0; i<10; i++ {
fmt.Printf("%q\n", <-ch)
}
fmt.Printf("Done\n")
}
This will get the correct result as shown below
"A 0"
"B 0"
"A 1"
"A 2"
"A 3"
"A 4"
"B 1"
"B 2"
"B 3"
"B 4"
Done
My question is, when I change the case statement, I got weird output. it seems some value were dropped, and of course no more value is received in fan-in channel and deadlock happened.
select {
case ch<- <-input1:
case ch<- <-input2:
}
Result is like this:
"B 0"
"A 1"
"B 2"
"A 3"
"A 4"
fatal error: all goroutines are asleep - deadlock!
Anyone has any idea of this situation?
That happens because in select only one channel read or write is non blocking.
All other operations behave normally.
So in this piece of code
select {
case ch<- <-input1:
case ch<- <-input2:
}
it receives a value from input1
(blocking). It waits for a delay and receives A 0
.
It tries to write it to ch
, non blocking.
If the code in main
was fast enough to reach
fmt.Printf("%q\n", <-ch)
line then write to a channel succeeds.
Then fanIn
for
loop starts second iteration: it picks the second case
this time (it's not deterministic). At this point it's likely that the second goroutine has written the B 0
value.
But there is a chance that the main
function loop has not consumed the value from the combined channel.
So the value is dropped.
This repeats multiple times, you lose few values and end up having no writers and a reader waiting for the remaining values forever.
This slightly modified copy of code demonstrates it: https://play.golang.org/p/lcM5OKx09Dj
I will explain why your value in channel has been dropped. Base on the fact in Golang document about execution of select statement:
For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the "select" statement. The result is a set of channels to receive from or send to, and the corresponding values to send. Any side effects in that evaluation will occur irrespective of which (if any) communication operation is selected to proceed. Expressions on the left-hand side of a RecvStmt with a short variable declaration or assignment are not yet evaluated.
The result is a set of channels to receive from or send to, because of this, when you pass channel in left-hand side, select statement first try to get ALL right-hand side statements are evaluated before trying to send to channel in left-hand side statement
Obviously, input1 and input2 must be received at one round of select statement, and then send to ch, so the ch channel Always has exact 5 value which mixed from intput1 and input2 Equally despite of their delay
You can see all data has printed when sending value to input1 and input2 channel in this code: Link
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With