You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于Go内存模型中无缓冲Channel接收同步顺序的疑问

无缓冲Channel同步顺序的疑惑解答

首先要明确两个核心概念的边界,这是解开矛盾的关键:

1. 操作的“完成”节点 vs “观察到”的关系

定义(2)中说“若同步类读操作r观察到同步类写操作w的结果,则w同步先于r”,这里的场景是单向的写-读流程:比如带缓冲的Channel,发送操作w把值写入缓冲区后就完成了,后续接收操作r从缓冲区读取这个值(也就是观察到w的结果),此时w的完成确实先于r的完成,符合定义描述。

而无缓冲Channel的发送和接收是同步交换流程:发送操作w并不是把值写入缓冲区就结束,它必须等待接收方拿到值后才算完成;接收操作r拿到值后,会触发发送方的w操作继续执行。所以这里的“同步先于”指的是:接收操作r的完成,先于发送操作w的完成——这和定义(2)并不冲突,因为定义(2)的前提是“r观察到w的结果”时w先于r,但无缓冲场景中,w的完成是在r完成之后,而r观察到w的值的同时,w还处于未完成状态。

2. 同步类操作的双向性

虽然Channel接收属于类读操作、发送属于类写操作,但无缓冲Channel的同步是双向的:发送方的写操作依赖接收方的读操作完成才能结束,这打破了“写先于读完成”的单向逻辑。内存模型专门明确这一点,是为了保证:接收方在拿到值后执行的所有操作,都能看到发送方在发送前的所有内存操作;同时发送方在发送完成后执行的操作,也能看到接收方在接收后的所有操作。

举个简单的例子:

var x int
ch := make(chan bool)

// 发送 goroutine
go func() {
    x = 1
    ch <- true // 发送操作w,直到接收方拿到值才完成
}()

// 接收 goroutine
func() {
    <-ch // 接收操作r,拿到值后完成
    println(x) // 必然打印1,因为r完成先于w完成,w完成前x已经被设为1
}()

这里接收操作完成后,发送操作才结束,所以接收方的println(x)能看到发送方设置的x=1,这正是“接收同步先于发送完成”带来的内存可见性保证。

内容的提问来源于stack exchange,提问作者Cosmos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 05:07:16