Go 内存模型与 Happens-Before
同步保证与内存排序
Go 内存模型与 Happens-Before 是 CoddyKit 上的免费 Go Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Go Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Go Academy 课程共包含 4 节课。
什么是内存模型?
Go 的内存模型规定了一个协程对变量的写入何时能够保证被另一个协程的读取看到。如果没有这些保证,并发程序可能会观察到过期或不一致的值。
先行发生关系
先行发生关系保证事件 A 之前的所有写入在事件 B 发生时都可见。如果不存在先行发生关系,读取可能观察到任意一次写入,包括零值。
有保证的先行发生关系:协程启动
在 go 语句之前发生的一切,都先行发生于协程函数的启动。创建的协程可以看到创建它之前完成的所有写入。
x := 10
go func() {
fmt.Println(x) // guaranteed to see 10
}()有保证的关系:通道发送与接收
向通道发送数据先行发生于对应的接收操作。关闭通道先行发生于接收其零值的操作。这些是主要的同步原语。
ch := make(chan int)
go func() { ch <- 42 }() // send hb receive
v := <-ch
fmt.Println(v) // guaranteed to see 42有保证的关系:sync.Mutex
互斥锁的第 n 次 Unlock 先行发生于第 n+1 次 Lock。这确保在解锁后进行加锁的协程可以看到解锁前完成的所有写入。
有保证的关系:sync.Once
once.Do 函数完成先行发生于任何一次 once.Do 调用返回。所有调用 once.Do 的协程都能看到初始化结果。
有保证的关系:通道关闭
关闭通道先行发生于从已关闭通道接收零值的操作。这就是已完成通道如何向多个协程广播的方式。
不存在先行发生关系:未受保护的共享变量
如果两个协程在没有同步的情况下访问同一个变量,且至少有一个协程执行写入,则行为未定义。竞态检测器可以捕获这种问题。
原子操作
atomic 包提供顺序一致性的操作。对于任何观察到所存储值的协程,对某个变量执行的原子存储先行发生于对同一变量执行的原子加载。
通道的内存模型
对于容量为 C 的带缓冲通道,第 k 次接收先行发生于第 k+C 次发送完成。这体现了有限缓冲容量所保证的背压。
实用规则
如果要在协程之间共享数据,请使用通道转移所有权,或使用互斥锁/原子操作保护读取和写入。不要在没有同步的情况下依赖对执行顺序的直觉判断。
快速检查
以下哪一项会在协程之间建立先行发生关系?
回顾:Go 内存模型
要点:
- 先行发生关系规定了写入何时能够跨协程可见
- 通道、互斥锁、一次性初始化和协程启动会创建 HB 边
- 对共享可变状态的非同步访问是未定义的
- 使用竞态检测器捕获违规操作
常见问题解答
「Go 内存模型与 Happens-Before」课时是免费的吗?
是的 — 「Go 内存模型与 Happens-Before」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Go Academy 课程的其余内容,请升级到 CoddyKit PRO。 Go Academy 课程共包含 4 节课。
「Go 内存模型与 Happens-Before」这节课中我会学到什么?
同步保证与内存排序 你通过在浏览器中直接运行的动手代码来练习 Go Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Go Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Go Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「Go 内存模型与 Happens-Before」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Go Academy 课中编写并运行代码吗?
能。每节 Go Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 栈与堆及逃逸分析
- Go 内存模型与 Happens-Before
- 垃圾回收器内部机制
- 减少内存分配:sync.Pool 与 Arenas