为什么在此通道上不发送阻止?

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?

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.