Go 为什么不';t在这个通道上发送阻塞?

Go 为什么不';t在这个通道上发送阻塞?,go,goroutine,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() {

考虑以下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)
        }
    }
}()
但是,当我实际运行该示例时,我最终得到:

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

输出结果表明,发送没有像预期的那样阻塞。这是怎么回事?

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

  • 第一个goroutine被
    Sleep()
    调用阻止
  • 第二个goroutine在通道上等待接收时被阻塞
  • Sleep()
    调用结束后,第一个goroutine将继续执行
  • “test1”
    被写入通道,从而阻塞
  • 第二个goroutine接收值,但在打印输出之前,执行切换回第一个goroutine
  • 第一个goroutine打印
    “发送test2…”
    ,并将第二个值写入通道,该通道将再次阻塞
  • 第二个goroutine在被抢占的位置恢复,并打印
    “Received:test1”
    消息
  • 循环再次执行并接收第二个值

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

您可以看到。你会注意到(至少在操场上)这些都倾向于成对分组(如Nathan所说,控制开关切换回发送器,以便在再次阻塞前记录第二行)。尽管可以说这是准确的,但我不会称之为比赛条件。而仅仅是调度器屈服点的一种表现。大多数人只在它导致错误行为时才称之为竞争条件,而此代码没有。
Sending test1...
Sending test2...
Received: test1
Received: test2