GO Memory Model

Stevennnmmm · · 436 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

引用:https://golang.org/ref/mem#tmp_0
本文只作为翻译 和一些补充,供笔者日后翻阅

在不同的goroutine中涉及到对同一个变量操作,我们简单成为并发,使用data race 检测的话也可以检测出来,那么这种应该称为bug的代码,我们还是应该细究一下

介绍/introduction

原文: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.
翻译:Go内存模型指定了一种条件,在这种条件下,可以保证在一个goroutine中读取变量可以观察到在不同goroutine中写入同一变量所产生的值。

建议/Advice

原文:Programs that modify data being simultaneously accessed by multiple goroutines must serialize such access.
To serialize access, protect the data with channel operations or other synchronization primitives such as those in the [`sync`](https://golang.org/pkg/sync/) and [`sync/atomic`](https://golang.org/pkg/sync/atomic/) packages.
If you must read the rest of this document to understand the behavior of your program, you are being too clever.
Don't be clever.
翻译:修改由多个goroutine同时访问的数据的程序必须序列化此类访问。
要序列化访问,请使用通道操作或其他同步原语(例如[`sync`](https://golang.org/pkg/sync/) 和[`sync/atomic`](https://golang.org/pkg/sync/atomic/)软件包中的原语)保护数据。
如果您必须阅读本文档的其余部分以了解程序的行为,那么您就太聪明了。
别自作聪明

补充:在我们使用go关键字进行数据访问,涉及到并发的我们尽可能使用sync/atomic/来保护数据,有时候不使用它们来保护数据也不会报错,但是还是不要自作聪明的去在代码上使用这种写法

发生之前/happen_before


原文:Within a single goroutine, reads and writes must behave as if they executed in the order specified by the
 program. That is, 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.

To specify the requirements of reads and writes, we define happens before, a partial order on the 
execution of memory operations in a Go program. If event e1 happens before event e2, then we say that
 e2 happens after e1. Also, if e1 does not happen before e2 and does not happen after e2, then we say 
that e1 and e2 happen concurrently.

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.
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.

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.

The initialization of variable v with the zero value for v's type behaves as a write in the memory model.

Reads and writes of values larger than a single machine word behave as multiple machine-word-sized 
operations in an unspecified order.

翻译:在单个goroutine中,读取和写入的行为必须像它们按照程序指定的顺序执行一样。也就是说,仅当
重新排序不会改变语言规范所定义的该goroutine中的行为时,编译器和处理器才可以对单个goroutine中执
行的读取和写入进行重新排序。由于此重新排序,一个goroutine观察到的执行顺序可能与另一个goroutine
察觉到的执行顺序不同。例如,如果执行一个goroutine,则另一个goroutinea = 1; b = 2;可能会在的更新
值b之前观察到的更新值a。

为了指定读写要求,我们在Go程序中定义 发生在之前的,这是执行内存操作的部分顺序。如果事件e 1发
生在事件e 2之前,那么我们说e 2发生在e 1之后。另外,如果ē 1不发生之前ë 2后不会发生ë 2,那么我们说,ë 1和Ë 2同时发生。

在单个goroutine中,事前发生顺序是程序表示的顺序。

读[R变量v被允许观察写W¯¯到v 如果满足以下两个条件成立:

r在w之前不会发生。
有没有其他的写W”来v那之后会发生W¯¯但在此之前[R 。
为了保证读[R变量的v观察特定写W¯¯到v,确保W¯¯是只写[R能够观看到。也就是说,如果同时满足以下两个条件,则保证r遵守w:

w在r之前发生。
对共享变量的任何其他写操作v 都发生在w之前或r之后。
这对条件比第一对要强。它要求没有其他写入与w或r同时发生。

在单个goroutine中,没有并发性,因此这两个定义是等效的:read r观察到最近写入w到的值v。当多个
goroutines访问一个共享变量时v,它们必须使用同步事件来确定事件发生的条件,以确保读取遵守所需的写入。

使用的类型v的零值初始化变量v的行为就像是在内存模型中进行写操作。

大于单个机器字的值的读取和写入将以未指定的顺序充当多个机器字大小的操作。

happen_before 不单单是golang 有,其实java也有。

需要注意的一个点:memory reording :内存重排,语言级别的大佬为了压榨cpu性能,会在语言编译的时候进行语言的重新排列,达到一样的效果,但是会性能更好。

-1. 内存重排有个前提:仅当重新排序不会改变语言规范所定义goroutine的行为的时候会发生重排

我用通俗一点的说法来说的话:
对单个goroutine中或者单个线程程序来说,对变量的读取和写入一定就是按照程序的逻辑来的。对于多线程来说大部分程序都会发生重排,因此一个goroutine观察到的执行顺序和另外一个goroutine执行顺序不同

  • 2.理解golang的并发?

在执行内存操作的时候,如果顺序是e1发生在e2之前,那么我们说e2发生在e1之后,那么如果说e2不发生在e1之前也不发生在e1之后,那么我们说e1,e2是并发操作的

  • 3.如何保证两个goroutine操作(一个对V变量读R,一个对变量W)顺序正确,正确逻辑应该是先写后读
    1.保证r在w之前不会发生。
    2.在w之前,r之后,没有写操作
    3.w发生在r之前
    4.w~r之间没有其他的任何的写操作
    在单个goroutine中,没有并发性,因此这两个定义是等效的:read r观察到最近写入w到的值v。当多个goroutines访问一个共享变量时v,它们必须使用同步事件来确定事件发生的条件,以确保读取遵守所需的写入

同步化/Synchronization

初始化/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.
程序初始化在单个goroutine中运行,但是该goroutine可能会创建其他同时运行的goroutine。
如果某个软件包p导入了package q,则q的init功能将在任何的开始之前完成 p。
main.main所有init功能完成后, 将启动功能。

这个主要将的init执行顺序

Go创建 /goroutine create

The go statement that starts a new goroutine happens before the goroutine's execution begins.
For example, in this program:
var a string
func f() {
    print(a)
}
func hello() {
    a = "hello, world"
    go f()
}
calling hello will print "hello, world" at some point in the future (perhaps after hello has returned).

翻译:

go启动新的goroutine 的语句发生在goroutine的执行开始之前。
例如,在此程序中:
var a string 
func f(){ 
    print(a)
} 
func hello(){ 
    a =“ hello,world” 
    go f()
}
调用hello将"hello, world" 在将来的某个时间(可能在hello返回后)打印。

这个案例实际就是将的goroutine的创建调用方法

Goroutine破坏/Goroutine destruction

The exit of a goroutine is not guaranteed to happen before any event in the program. For example, in this program:
var a string
func hello() {
    go func() { a = "hello" }()
    print(a)
}
the assignment to a is not followed by any synchronization event, so it is not guaranteed to be observed 
by any other goroutine. In fact, an aggressive compiler might delete the entire go statement.
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.

翻译:

不能保证goroutine的退出会在程序中发生任何事件之前发生。例如,在此程序中:
var a string 
func hello(){ 
    go func(){a =“ hello”}()
    print(a)
}
分配给时a,不会发生任何同步事件,因此不能保证任何其他goroutine都会观察到该事件。实际上,积极
的编译器可能会删除整个go语句。
如果必须通过另一个goroutine来观察goroutine的影响,请使用同步机制(例如锁定或通道通信)来建立相
对顺序。

这个例子实际上很有讲解价值,这个在go方法在编译器层面是不会把它创建出来的,这个就是并发的产生的事情,不能保证a的r 一定在w之后

后面的是关于chan,lock,once等实例的讲解,详情还是可以看一下官方文档


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:Stevennnmmm

查看原文:GO Memory Model

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

436 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传