Go——內(nèi)存模型

什么是內(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.

  1. 如果包p import 了包q,那p包運(yùn)行必須在q包init函數(shù)執(zhí)行完之后(q包init函數(shù) happens before p包全部運(yùn)行)
  2. 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)
}
  1. 第 n 次的 m.Unlock 一定 happens before 第 n+1 m.Lock 方法的返回;
  2. 對(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)求到讀鎖。
  3. 對(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í)行完成。

Reference

Go內(nèi)存模型

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容