Go——内存模型

什么是内存模型

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.

内存模型指导的是不同goroutine对内存中同一变量进行读写操作对同步性。

Happens Before

Within a single goroutine, the happens-before order is the order expressed by the program.

在单一goroutine的情况下,happends-before 的顺序就是程序锁表达的顺序

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.

如果要保证对“变量 v 的读操作 r”能够观察到一个对“变量 v 的写操作 w”,并且 r 只能观察到 w 对变量 v 的写,没有其它对 v 的写操作,也就是说,我们要保证 r 绝对能观察到 w 操作的结果,那么就需要同时满足两个条件:w happens before r;其它对 v 的写操作(w2、w3、w4, …) 要么 happens before w,要么 happens after r,绝对不会和 w、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.

对于多个goroutine访问共享内存下的变量v,必须使用同步原语(synchronization events)来保证读写顺序。

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.

读写一个大于一个机器字节(machine word,64位系统位64bit,32位则位32bit)的操作可以看作是随机的(即不可保证顺序的正确性)

Synchronization

Init函数

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包运行必须在q包init函数执行完之后(q包init函数 happens before p包全部运行)
  2. main.main 函数 必须在 init函数完成之后执行 (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语句 happens before go出去的goroutine代码执行,即上述代码能保证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"的值
这个时候就需要用到其他同步原语(synchronization mechanism)来确保你想要的顺序。

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)
}

在一个有缓冲区的channel上,同一次序(比如手都是第2个)的发送操作 happen before 接受操作。即当channel没有数据时,<-chan 会阻塞,只有当有数据发送完成时,接受操作才会继续往下执行

The closing of a channel happens before a receive that returns a zero value because the channel is closed

即上述的代码中将 c <- 0替换为close(c)也能达到同样的效果。

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)
}

在一个无缓冲区的channel上,接受操作是 happen before 发送操作的,即发送操作会一直阻塞等待有其他goroutine来接受并完成时才会继续执行。

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. 对于读写锁 RWMutex m,如果它的第 n 个 m.Lock 方法的调用已返回,那么它的第 n 个 m.Unlock 的方法调用一定 happens before 任何一个 m.RLock 方法调用的返回,只要这些 m.RLock 方法调用 happens after 第 n 次 m.Lock 的调用的返回。这就可以保证,只有释放了持有的写锁,那些等待的读请求才能请求到读锁。
  3. 对于读写锁 RWMutex m,如果它的第 n 个 m.RLock 方法的调用已返回,那么它的第 k (k<=n)个成功的 m.RUnlock 方法的返回一定 happens before 任意的 m.RUnlockLock 方法调用,只要这些 m.Lock 方法调用 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函数执行完成的,即doprint函数中必然能保证print(a)这行执行的时候,setup函数已经执行完成。

Reference

Go内存模型

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 223,604评论 6 521
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 95,650评论 3 401
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 170,578评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,469评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,486评论 6 399
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,030评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,414评论 3 426
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,382评论 0 278
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,920评论 1 322
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,967评论 3 343
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,104评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,758评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,433评论 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,920评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,040评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,590评论 3 380
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,134评论 2 362

推荐阅读更多精彩内容