go内存模型

介绍 Go内存模型指定了一种条件,在这种条件下,可以保证读取一个goroutine中的变量,以观察不同goroutine中写入同一变量所产生的值。 建议 修改多个goroutine同时访问的数据必须序列化访问 序列化访问保护数据使用channel操作或者其他同步原语比如sync或者sync/atomic包 Happens before 在一个goroutine中,读写必须表现得就像它们按照程序指定的顺序执行一样;也就是说 在一个goroutine中,处理器和编译器可以重排读写的执行顺序,仅当重排后的行为不改变语言的设定.因为重排,一个goroutine观察到的执行顺序可能与另一个goroutine观察到的顺序不同.举个例子,如果一个goroutine执行a = 1; b = 2;,另一个可能会观察到b在a之前更新. 为了指定读写的需要,我们定义了在Go程序中执行内存操作的偏序(partial order)。如果事件$e_1$先发生于事件$e_2$我们说$e_2$后发生于$e_1$. 同样的如果$e_1$没有先发生于$e_2$并且$e_1$没有后发生于$e_2$那么我们说$e_1 e_2$同时发生. 在单个gorutine里, happens-before的顺序是程序表示的顺序. 如果下面两个条件成立,则允许变量v的读r 观察到对v的写w: r没有先发生于w 没有其他对v的写w’ ,后发生于w, 先发生于r (也就是在w 和 r之间不存在 w') 为了保证r能读到w的写,要确保w是允许r观察的唯一写入.也就是说,如果以下两个条件均成立,则保证r观察到w: w先发生于r 任何其他写入共享变量v都要先发生于w或者后发生于r 这对条件比第一对更严格,这一对要求没有其他的写同时发生于w或r 在单个gorutine中没有并发,这两个定义是等价的:读r观察最近写入w到v的值. 在多个gorutine访问一个共享变量v,必须使用同步事件来建立happens-before条件来确保读到期望的写. 变量v的类型为零值时,变量v的初始化行为就像在内存模型中写入一样。(初始化等同于写入) 读写超过机器字(machine word)的行为就和以未指定的顺序执行多个机器字大小的操作一样(超过machine word 每个machine word 读取和写入顺序可能不是期望的) 总之, 想要在一个gorutine中读到另一个gorutine的写就要保证, 写在读之前发生,如果我们不加控制,这个写先于读的顺序就很难保证,所以我们需要使用atomic或者和lock机制来保证顺序保证happens-before 同步 初始化 程序初始化在单个goroutine中运行,但该goroutine可能会创建其他并发运行的goroutine。 如果包p导入包q,则q的init函数的完成时间在任何p的开始之前。 函数main的开始。main在所有init函数完成后发生。 Goroutine创建 启动新goroutine的go语句发生在goroutine开始执行之前。 举例 var a string func f() { print(a) } func hello() { a = "hello, world" go f() } 调用hello将在将来hello, world,可能hello已经返回 Goroutine销毁 goroutine的退出不能保证先发生于任何事件,例如: ...

<span title='2022-06-23 10:33:29 +0800 +0800'>六月 23, 2022</span>