How does channel blocking work in Go?

17,821

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.

Share:
17,821
user1928896
Author by

user1928896

Updated on June 07, 2022

Comments

  • user1928896
    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
    user1928896 over 8 years
    Thanks for the clarification.
  • Michael Laszlo
    Michael Laszlo over 8 years
    Thanks for the upvote. I am a bit irked because I posted my answer first.
  • user1928896
    user1928896 over 8 years
    I chose the other answer since it included the reason for the program exiting even before the go routines finishing their job.
  • Michael Laszlo
    Michael Laszlo over 8 years
    I 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 on c1 or c2."
  • d219
    d219 about 6 years
    Links 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
    Rambatino over 5 years
    This is very unhelpful
  • Samir Kape
    Samir Kape almost 3 years
    @MichaelLaszlo After seeing your comment, I cancelled my upvote on accepted answer and upvoted yours.