Go 通道写入顺序保证

Go channel write ordering guarantees

给定以下代码:

ch1 := make(chan struct{}, 1)
ch2 := make(chan struct{})
ready := make(chan struct{})
done := make(chan struct{})

go func() {
    close(ready)
    select {
    case <-ch1:
        fmt.Println("ch1")
    case <-ch2:
        fmr.Println("ch2")
    }
}()

<-ready

ch1 <- struct{}{}
close(ch2)

<-done

是否保证始终打印“ch1”?或者是否有可能因为 ch1 被缓冲并且 ch2 立即关闭,第二个 case 可以先 运行?

文档/代码中是否有任何参考来验证此行为?

写入 ch1 不会立即导致 select 操作继续。 goroutine 可以在通道写入之后或关闭操作之后进行调度,因此这两种情况都有机会 运行。如果 ch1 是无缓冲的,那么写入操作只会在通道读取之后发生,因此程序将始终打印 ch1。

不,不能保证第二种情况不会发生。

当您使用通道时——通道是同步两个 goroutine 的理想选择——您正在操作两个独立的通道。所以不能保证主 goroutine 对无缓冲 ch1 通道的写入会立即将控制权交给第二个 goroutine 的 select 语句。

ch2 的关闭也可能发生在调度程序将控制权交给其他 goroutine 之前 - 然后 select 将随机选择两个可能的路径。

可能很容易相信,但在不同的负载条件下会导致危险的假设。


查看 Go Scheduler Design doc 的早期文档不会有太大帮助,因为时间表的实施细节会随着 Go 语言的发布而发生变化。

最后,需要两个独立通道之间的协调来保证处理顺序。