The Go Memory Model #
Happens Before #
Compilers and processors may reorder the reads and writes executed within a single goroutine only when the reordering does not change the behavior within that goroutine as defined by the language specification. Because of this reordering, the execution order observed by one goroutine may differ from the order perceived by another. For example, if one goroutine executes a = 1; b = 2;
, another might observe the updated value of b
before the updated value of a
.
首先我们要知道:在不改变程序运行结果的前提下,compilers 和 processors 会对程序的运行指令做重排序。这种重排序在单线程的情况下是没有问题的,但是在多线程的情况下,这种重排序可能会导致输出一些意想不到的结果(比如一个线程运行 a = 1; b = 2;
,另外一个线程运行 print(b); print(a)
,因为指令重排序的缘故,可能会看到b输出是2,而a输出是0)
Within a single goroutine, the happens-before order is the order expressed by the program.
A read r of a variable v
is allowed to observe a write w to v
if both of the following hold:
- r does not happen before w.
- There is no other write w' to
v
that happens after w but before r.
A read r is guaranteed to observe a write w if both of the following hold:
- w happens before r.
- Any other write to the shared variable
v
either happens before w or after r.
This pair of conditions is stronger than the first pair; it requires that there are no other writes happening concurrently with w or r.
Within a single goroutine, there is no concurrency, so the two definitions are equivalent: a read r observes the value written by the most recent write w to v
. When multiple goroutines access a shared variable v
, they must use synchronization events to establish happens-before conditions that ensure reads observe the desired writes.
happens-before 是一个术语,它定义了两个操作间的一种偏序关系,且这种关系具有传递性。有两个操作 A 和 B:
- 如果 A happens-before B,则 B happens-after A,且 A 操作对内存的影响将在执行 B 操作之前可见
- 如果 A happens-before B,B happens-before C,那么 A happens-before C
- 如果 A 和 B 之间没有任何的 happens-before 关系,那么 A 和 B happen concurrently。
如果 A 操作 和 B 操作在同一个线程里,那么他们 happens-before 关系就是他们的代码声明顺序(比如A操作的声明在B之前,那么A happens-before B)
如果 A 操作 和 B 操作在不同的线程里,那么他们之前可能存在 happens-before 关系(通过同步事件控制),也可能不存在(即happen concurrently)
Synchronization #
以下是 Go 中定义的 Happens Before 保证
1. Initialization #
If a package p
imports package q
, the completion of q
’s init
functions happens before the start of any of p
’s.
The start of the function main.main
happens after all init
functions have finished.
2. Goroutine creation #
The go
statement that starts a new goroutine happens before the goroutine’s execution begins.
3. Goroutine destruction #
The exit of a goroutine is not guaranteed to happen before any event in the program.
goroutine 的退出不能保证 happens-before 程序中的任何事件
4. Channel communication #
Channel communication is the main method of synchronization between goroutines.
-
A send on a channel happens before the corresponding receive from that channel completes.
-
The closing of a channel happens before a receive that returns a zero value because the channel is closed.
-
A receive from an unbuffered channel happens before the send on that channel completes.
-
The kth receive on a channel with capacity C happens before the k+Cth send from that channel completes.
channel 的 happens-before 在Go的内存模型中的4种情况:
- channel 中的 send 操作 happens-before 于 receive 操作
- channel 的关闭 happens-before 于 receive 操作
- unbuffered channel 中的 receive 操作 happens-before 于 send 操作
- 容量为 C 的 channel 的第 k 个 receive 操作,happens-before 于向该 channel 的第 k + C 个写 send 操作的完成
5. Locks #
The sync
package implements two lock data types, sync.Mutex
and sync.RWMutex
.
-
For any
sync.Mutex
orsync.RWMutex
variablel
and n < m, call n ofl.Unlock()
happens before call m ofl.Lock()
returns. -
For any call to
l.RLock
on async.RWMutex
variablel
, there is an n such that thel.RLock
happens (returns) after call n tol.Unlock
and the matchingl.RUnlock
happens before call n+1 tol.Lock
.
对于任意的 sync.Mutex 或 sync.RWMutex 变量 l 并且 n < m,对第 n 个 l.Unlock() 的调用先行发生于第 m 个 l.Lock() 的返回
一个 sync.RWMutex 变量 l,对于任何 l.RLock 的调用,存在一个 n,使得第 n 个 l.RLock 后来发生于第 n 个的 l.Unlock,同时该 RLock 所对应的 l.RUnlock 先行发生于第 n+1 个 l.Lock
6. Once #
A single call of f()
from once.Do(f)
happens (returns) before any call of once.Do(f)
returns.
通过 once.Do(f) 的对 f() 的单次调用,happens-before 于所有其他 once.Do(f) 的返回