为什么不阻塞此通道上的发送?

Why aren't sends on this channel blocking?

考虑以下 Go 代码片段:

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)
        }
    }
}()

完整来源: http://play.golang.org/p/yY6JQzDMvn

我希望第一个 goroutine 在尝试将 "test1" 写入通道时会阻塞,直到第二个 goroutine 接收为止。预期输出为:

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

然而,当我实际 运行 这个例子时,我最终得到:

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

输出显示发送没有像预期的那样阻塞。这是怎么回事?

这是由两个 goroutine 中的竞争条件引起的。默认情况下,Go 运行时对所有 goroutine 使用单个线程,因此执行是序列化的。考虑以下执行上述代码的可能场景:

  • 第一个 goroutine 被 Sleep() 调用阻塞
  • 第二个 goroutine 被阻塞等待通道上的接收
  • 第一个goroutine在Sleep()调用结束后继续执行
  • "test1" 写入通道,阻塞
  • 第二个 goroutine 接收值 但在打印输出之前,执行切换回第一个 goroutine
  • 第一个 goroutine 打印 "Sending test2..." 并将第二个值写入再次阻塞的通道
  • 第二个 goroutine 在它被抢占的地方恢复并打印 "Received: test1" 消息
  • 循环再次执行并接收到第二个值

综上所述,send确实是阻塞的,只是看起来不是输出顺序的问题。