为什么不发送此频道阻止?

时间:2015-05-28 22:33:43

标签: go goroutine

考虑以下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

输出会显示发送没有像预期的那样阻塞。这里发生了什么?

1 个答案:

答案 0 :(得分:5)

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

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

总之,发送确实是阻塞的,它似乎并不是由于输出的顺序。