Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does channel blocking work in Go?

Tags:

select

go

channel

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?

like image 813
user1928896 Avatar asked Sep 12 '15 11:09

user1928896


People also ask

What happens when you close a channel in Go?

Closing a channel indicates that no more values will be sent on it. This can be useful to communicate completion to the channel's receivers. In this example we'll use a jobs channel to communicate work to be done from the main() goroutine to a worker goroutine.

How does channel work in Golang?

In Golang, or Go, channels are a means through which different goroutines communicate. Think of them as pipes through which you can connect with different concurrent goroutines. The communication is bidirectional by default, meaning that you can send and receive values from the same channel.

What does blocking mean in Golang?

Blocking Statements This means: A statement to receive data from a channel will block until some data is received. A statement to send data to a channel will wait until the sent data has been received.

Are Go channels buffered?

Golang provides buffered channels, which allow you to specify a fixed length of buffer capacity so one can send that number of data values at once. These channels are only blocked when the buffer is full. Likewise, the channel on the receiving end will only block when the buffer is empty.


2 Answers

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.

like image 198
Filipe Gonçalves Avatar answered Sep 21 '22 06:09

Filipe Gonçalves


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.

like image 21
Michael Laszlo Avatar answered Sep 20 '22 06:09

Michael Laszlo