How does channel blocking work in Go?
Solution 1
This is related to how select
statements work in Go.
From the Go documentation on select
:
If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. Otherwise, if there is a default case, that case is chosen. If there is no default case, the "select" statement blocks until at least one of the communications can proceed.
So, without a default case, the code will block until some data is available in either of the channels. It implicitly waits for the other goroutines to wake up and write to their channel.
When you add the default case, it is very likely that the select
statement is reached before the other goroutines wake up from sleeping.
So, since there is no data available (yet), and there is a default case, the default case is executed. This is done twice, and it takes less than 1 second. So the program ends up terminating before any of the go routines have a chance to wake up and write to the channel.
Note that this is technically a race condition; there is absolutely no guarantee that the 2 iterations of the loop will run before any of the go routines wake up, so in theory it is possible to have a different output even with a default case, but in practice it is extremely unlikely.
Solution 2
The select
statement blocks until at least one case is ready. The Go language specification reads, in part:
If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. Otherwise, if there is a default case, that case is chosen. If there is no default case, the "select" statement blocks until at least one of the communications can proceed.
In the original code, the default
case is ready on both iterations of the loop because there is a delay before anything is sent on c1
or c2
.
After you remove the default
case, the select
statement must wait for data to be available in c1
or c2
.
user1928896
Updated on June 07, 2022Comments
-
user1928896 almost 2 years
I'm learning the Go language. Here is an example I've come across. Can someone please explain what is happening here?
package main import "time" import "fmt" func main() { c1 := make(chan string) c2 := make(chan string) go func() { time.Sleep(time.Second * 1) c1 <- "one" }() go func() { time.Sleep(time.Second * 2) c2 <- "two" }() for i := 0; i < 2; i++ { select { case msg1 := <-c1: fmt.Println("received", msg1) case msg2 := <-c2: fmt.Println("received", msg2) default: fmt.Println("Default") } } }
Output:
Default Default Program Exited
If I comment out the default section
//default: // fmt.Println("Default")
the output becomes:
received one received two Program exited.
How does the presence of the
default
case change the way channel blocking works? -
user1928896 over 8 yearsThanks for the clarification.
-
Michael Laszlo over 8 yearsThanks for the upvote. I am a bit irked because I posted my answer first.
-
user1928896 over 8 yearsI chose the other answer since it included the reason for the program exiting even before the go routines finishing their job.
-
Michael Laszlo over 8 yearsI thought I covered it here: "In the original code, the
default
case is ready on both iterations of the loop because there is a delay before anything is sent onc1
orc2
." -
d219 about 6 yearsLinks are useful but it's better if you can post the relevant code to as if the links die your answer becomes useless, thanks.
-
Rambatino over 5 yearsThis is very unhelpful
-
Samir Kape almost 3 years@MichaelLaszlo After seeing your comment, I cancelled my upvote on accepted answer and upvoted yours.