关于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
相关产品推荐
相关产品推荐

