Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chained channel operations in a single `select` case

Tags:

go

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?

like image 910
YUTING LAI Avatar asked Dec 17 '22 22:12

YUTING LAI


2 Answers

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

like image 150
zerkms Avatar answered Jan 19 '23 19:01

zerkms


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

like image 23
Hau Ma Avatar answered Jan 19 '23 18:01

Hau Ma