Why aren't sends on this channel blocking?

Issue

Consider the following snippet of Go code:

c := make(chan string)

go func() {
    time.Sleep(100 * time.Millisecond)
    fmt.Println("Sending test1...")
    c <- "test1"
    fmt.Println("Sending test2...")
    c <- "test2"
    fmt.Println("Done sending")
}()

go func() {
    for {
        select {
        case n := <-c:
            fmt.Println("Received:", n)
        }
    }
}()

Full source: http://play.golang.org/p/yY6JQzDMvn

I expect that the first goroutine will block when attempting to write "test1" to the channel until the receive in the second goroutine. The expected output would be:

Sending test1...
Received: test1
Sending test2...
Received: test2

However, when I actually run the example, I end up with:

Sending test1...
Sending test2...
Received: test1
Received: test2

It would appear by the output that the send is not blocking as one would expect. What’s going on here?

Solution

This is caused by a race condition in the two goroutines. By default, the Go runtime uses a single thread for all goroutines, so execution is serialized. Consider the following possible scenario for executing the code above:

  • the first goroutine is blocked by a Sleep() call
  • the second goroutine is blocked waiting for a receive on the channel
  • the first goroutine resumes execution after the Sleep() call ends
  • "test1" is written to the channel, which blocks
  • the second goroutine receives the value but before printing the output, execution switches back to the first goroutine
  • the first goroutine prints "Sending test2..." and writes the second value to the channel which again blocks
  • the second goroutine resumes where it was preempted and prints the "Received: test1" message
  • the loop is executed again and receives the second value

In summary, the send is indeed blocking, it just doesn’t appear to be due to the order of output.

Answered By – Nathan Osman

Answer Checked By – Jay B. (GoLangFix Admin)

Leave a Reply

Your email address will not be published.