原文 The Go Memory Model
Go的内存模型描述的是”在一个 groutine中 对变量进行读操作能够侦测到在其他goroutine中对该变量的写操作”的条件。
Happens Before
对变量 v 的读 read r 要观测到 write w 对变量 v 的写,需要同时满足:
- r 不发生在 w 之前
- 没有其他的 w’ 对变量 v 的写发生在 w 之后, r 之前.
为了保证 read 观察到唯一一个 write w.必须同时满足:
- w 发生在 r之前
- 其他 write 发生在 w 之前或者 r 之后
这对条件比前一对更严格,要求没有其他的 write 没有与 w 或者 r 并发执行.
在单个 goroutine 中没有并发,是满足这两个条件的,保证 r 能看到 w 对 v 的写入.在多个 goroutine 共享变量 v 时,必须使用同步事件
满足 happens-before 条件确保 read 能读取到期望的 writes.
同步
goroutine 创建
The go statement that starts a new goroutine happens before the goroutine’s execution begins
1 | package main |
调用 hello
将会打印 “hello world”,可能发生在 hello
return 之前或者之后.
channel 通信
A send on a channel happens before the corresponding receive from that channel completes. 在一个channel中,发送 happens-before 相应的接收完成之前.
The closing of a channel happens before a receive that returns a zero value because the channel is closed. 关闭一个 channel happens-before 接收返回的 0 值之前.为 0 值是因为 channel 被关闭.
1 | package main |
A receive from an unbuffered channel happens before the send on that channel completes. 无缓冲 channel 的接收 happens-before 在发送完成之前.
1 | package main |
The kth receive on a channel with capacity C happens before the k+Cth send from that channel completes. 容量为 C 的 channel 第 k 次接收 happens-before k+C 次发送完成之前.无缓存通道就是C 为 0 时的特殊情况.
不正确的同步
1 | var a string |
对于主线程来说, setup 里面的执行顺序是不一致的, done = true 可能发生在 a 赋值之前,更糟糕的是, setup goroutine 中对 done 的修改,对
主线程来说可能是不可见的,将导致主线程死循环.
解决同步问题的思路:使用显示的同步机制.