什么是內(nèi)存模型
The Go memory model specifies the conditions under which reads of a variable in one goroutine can be guaranteed to observe values produced by writes to the same variable in a different goroutine.
內(nèi)存模型指導(dǎo)的是不同goroutine對(duì)內(nèi)存中同一變量進(jìn)行讀寫操作對(duì)同步性。
Happens Before
Within a single goroutine, the happens-before order is the order expressed by the program.
在單一goroutine的情況下,happends-before 的順序就是程序鎖表達(dá)的順序
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.
To guarantee that a read r of a variable v observes a particular write w to v, ensure that w is the only write r is allowed to observe. That is, r is guaranteed to observe 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.
如果要保證對(duì)“變量 v 的讀操作 r”能夠觀察到一個(gè)對(duì)“變量 v 的寫操作 w”,并且 r 只能觀察到 w 對(duì)變量 v 的寫,沒(méi)有其它對(duì) v 的寫操作,也就是說(shuō),我們要保證 r 絕對(duì)能觀察到 w 操作的結(jié)果,那么就需要同時(shí)滿足兩個(gè)條件:w happens before r;其它對(duì) v 的寫操作(w2、w3、w4, …) 要么 happens before w,要么 happens after r,絕對(duì)不會(huì)和 w、r 同時(shí)發(fā)生,或者是在它們之間發(fā)生。
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.
對(duì)于多個(gè)goroutine訪問(wèn)共享內(nèi)存下的變量v,必須使用同步原語(yǔ)(synchronization events)來(lái)保證讀寫順序。
The initialization of variable v with the zero value for v's type behaves as a write in the memory model.
變量v的零值初始化可以看作是一次寫操作
Reads and writes of values larger than a single machine word behave as multiple machine-word-sized operations in an unspecified order.
讀寫一個(gè)大于一個(gè)機(jī)器字節(jié)(machine word,64位系統(tǒng)位64bit,32位則位32bit)的操作可以看作是隨機(jī)的(即不可保證順序的正確性)
Synchronization
Init函數(shù)
Initialization
Program initialization runs in a single goroutine, but that goroutine may create other goroutines, which run concurrently.
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.
- 如果包p import 了包q,那p包運(yùn)行必須在q包init函數(shù)執(zhí)行完之后(q包init函數(shù) happens before p包全部運(yùn)行)
- main.main 函數(shù) 必須在 init函數(shù)完成之后執(zhí)行 (init happens before main)
Goroutine creation
The go statement that starts a new goroutine happens before the goroutine's execution begins.
var a string
func f() {
print(a)
}
func hello() {
a = "hello, world"
go f()
}
go語(yǔ)句 happens before go出去的goroutine代碼執(zhí)行,即上述代碼能保證a先賦值(a= "hello, world"),然后在讀a的值(print(a))。
Goroutine destruction
The exit of a goroutine is not guaranteed to happen before any event in the program.
If the effects of a goroutine must be observed by another goroutine, use a synchronization mechanism such as a lock or channel communication to establish a relative ordering
var a string
func hello() {
go func() { a = "hello" }()
print(a)
}
goroutine的退出是不能被保證happen before的,上述的代碼是不能保證print(a)可以讀到"hello"的值
這個(gè)時(shí)候就需要用到其他同步原語(yǔ)(synchronization mechanism)來(lái)確保你想要的順序。
Channel communication
buffered channel
A send on a channel happens before the corresponding receive from that channel completes.
var c = make(chan int, 10)
var a string
func f() {
a = "hello, world"
c <- 0
}
func main() {
go f()
<-c
print(a)
}
在一個(gè)有緩沖區(qū)的channel上,同一次序(比如手都是第2個(gè))的發(fā)送操作 happen before 接受操作。即當(dāng)channel沒(méi)有數(shù)據(jù)時(shí),<-chan 會(huì)阻塞,只有當(dāng)有數(shù)據(jù)發(fā)送完成時(shí),接受操作才會(huì)繼續(xù)往下執(zhí)行
The closing of a channel happens before a receive that returns a zero value because the channel is closed
即上述的代碼中將 c <- 0替換為close(c)也能達(dá)到同樣的效果。
unbuffered channel
A receive from an unbuffered channel happens before the send on that channel completes.
var c = make(chan int)
var a string
func f() {
a = "hello, world"
<-c
}
func main() {
go f()
c <- 0
print(a)
}
在一個(gè)無(wú)緩沖區(qū)的channel上,接受操作是 happen before 發(fā)送操作的,即發(fā)送操作會(huì)一直阻塞等待有其他goroutine來(lái)接受并完成時(shí)才會(huì)繼續(xù)執(zhí)行。
Lock
The sync package implements two lock data types, sync.Mutex and sync.RWMutex.
For any sync.Mutex or sync.RWMutex variable l and n < m, call n of l.Unlock() happens before call m of l.Lock() returns.
var l sync.Mutex
var a string
func f() {
a = "hello, world"
l.Unlock()
}
func main() {
l.Lock()
go f()
l.Lock()
print(a)
}
- 第 n 次的 m.Unlock 一定 happens before 第 n+1 m.Lock 方法的返回;
- 對(duì)于讀寫鎖 RWMutex m,如果它的第 n 個(gè) m.Lock 方法的調(diào)用已返回,那么它的第 n 個(gè) m.Unlock 的方法調(diào)用一定 happens before 任何一個(gè) m.RLock 方法調(diào)用的返回,只要這些 m.RLock 方法調(diào)用 happens after 第 n 次 m.Lock 的調(diào)用的返回。這就可以保證,只有釋放了持有的寫鎖,那些等待的讀請(qǐng)求才能請(qǐng)求到讀鎖。
- 對(duì)于讀寫鎖 RWMutex m,如果它的第 n 個(gè) m.RLock 方法的調(diào)用已返回,那么它的第 k (k<=n)個(gè)成功的 m.RUnlock 方法的返回一定 happens before 任意的 m.RUnlockLock 方法調(diào)用,只要這些 m.Lock 方法調(diào)用 happens after 第 n 次 m.RLock。
Once
A single call of f() from once.Do(f) happens (returns) before any call of once.Do(f) returns.
var a string
var once sync.Once
func setup() {
a = "hello, world"
}
func doprint() {
once.Do(setup)
print(a)
}
func twoprint() {
go doprint()
go doprint()
}
once.Do(func) 是 happen after func函數(shù)執(zhí)行完成的,即doprint函數(shù)中必然能保證print(a)這行執(zhí)行的時(shí)候,setup函數(shù)已經(jīng)執(zhí)行完成。