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. express
Go内存模型限定了一些条件 知足这些条件 才能让变量 安全地在不一样的goroutine之间读写 安全
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. app
a = 1; b = 2;
若是只有一个goroutine 读写行为和它们在程序中的位置一致 就是说 编译器和处理器只能在保证读写在程序中的正确行为时 好比读写顺序 才有可能对读写进行优化 举例来讲 在一个goroutine中执行 另外一个goroutine中可能会在a以前察觉到b的更新操做 ide
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 e1happens 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 e2happen concurrently. 函数
为了定义读写的需求 咱们给Go程序的内存操做 定义了一个偏序“发生在X以前” 若是时间e1发生在e2以前 那么咱们认为e2事件出如今e1后 一样的 若是e1不出如今e2以前 而且又不出如今e2以后 那么咱们认为e1和e2是并行的 oop
Within a single goroutine, the happens-before order is the order expressed by the program. 优化
在同一个goroutine中 “发生在X以前”这个次序和程序中表达出来的次序是一致的 ui
A read r of a variable v is allowed to observe a write w to v if both of the following hold: this
对v的写操做w 若是想被v的读操做r观察到 须要知足下面两个条件: spa
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:
想要保证读操做r 能够观察到特定的w写操做 须要确保w是r惟一能够观察到的写操做 也就是说 只有知足下面条件后 才得以保证r观察到w
This pair of conditions is stronger than the first pair; it requires that there are no other writes happening concurrently with w or 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的最近一次写 当多个goroutine同时访问共享变量v时 它们须要利用同步事件来创建“发生在X以前”这个条件 来确保读能够观察到写
The initialization of variable v with the zero value for v's type behaves as a write in the memory model.
v变量用对应类型的零值初始化行为 在内存模型中 和写变量v是相似的
Reads and writes of values larger than a single machine word behave as multiple machine-word-sized operations in an unspecified order.
若是读写的值大于一个机器字 它们的行为和 多机器字操做相似 次序不肯定
Program initialization runs in a single goroutine, but that goroutine may create other goroutines, which run concurrently.
程序初始化 是在单一的goroutine环境进行的 可是 这个goroutine能够再建立其它的goroutine 它们之间并行执行
If a package p imports package q, the completion of q's init functions happens before the start of any of p's.
若是包p导入了包q q的init函数在q的初始化以前执行
The start of the function main.main happens after all init functions have finished.
main.main函数在全部的初始化函数init结束后才会执行
The go statement that starts a new goroutine happens before the goroutine's execution begins.
go语句会开启一个新的goroutine 而且在这个goroutine执行前就建立好了
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).
在某个场合下调用hello函数 会打印“hello,world” (多是在hello函数已经返回的状况下)
The exit of a goroutine is not guaranteed to happen before any event in the program. For example, in this program:
咱们肯定不了goroutine在退出的次序 好比在事件e以前它必须退出 举例来讲:
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.
上面这段代码 a的赋值操做并无跟在任何的同步事件以后 因此并不能保证它会被其它的goroutine观察到 事实上 激进一点的编译器会把这段代码(go func(){a=fasd})删掉
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之间观察到变量的更新操做 那么须要使用一些同步的机制 好比使用锁 或者经过channel来通讯 从而创建起相对的读写次序
Channel communication is the main method of synchronization between goroutines. Each send on a particular channel is matched to a corresponding receive from that channel, usually in a different goroutine.
使用channel进行通讯 是在不一样的goroutine间同步的主要方法 每一个对特定channel的send操做 一般都会对应于其它goroutine中的receive操做
A send on a channel happens before the corresponding receive from that channel completes.
channel的send操做 必定发生在对应的receive操做完成以前
This program: 下面这段代码:
var c = make(chan int, 10) var a string func f() { a = "hello, world" c <- 0 } func main() { go f() <-c print(a) }
is guaranteed to print "hello, world". The write to a happens before the send on c, which happens before the corresponding receive on c completes, which happens before the print.
能够确保 打印“hello, world” 写操做发生在channel c的send操做以前 send又发生在c的receive操做以前 receive发生在print以前
The closing of a channel happens before a receive that returns a zero value because the channel is closed.
关闭channel发生在接受以前 由于channel已经关闭了 mai中go f() 开启了另外一个goroutine 在main中继续执行的话 会执行到 <-c 而后在channel接收这阻塞 goroutine f() 开始执行 写变量a 以后关闭channel 回到main 打印a channel在这里纯属是用来同步的
In the previous example, replacing c <- 0 with close(c) yields a program with the same guaranteed behavior.
上面那段代码中 若是用close(c)来替换c<-0 它的行为是同样的
A receive from an unbuffered channel happens before the send on that channel completes.
从无缓冲channel中接收操做 发生在该channel的发送操做结束前
This program (as above, but with the send and receive statements swapped and using an unbuffered channel):
下面这段代码 和上面代码同样 可是接收和发送操做掉了个 而且使用无缓冲channel:
var c = make(chan int) var a string func f() { a = "hello, world" <-c }
func main() { go f() c <- 0 print(a) }
is also guaranteed to print "hello, world". The write to a happens before the receive on c, which happens before the corresponding send on c completes, which happens before the print.
这段代码一样能够保证打印出“hello world” 对a的写操做 发生在channel c的接收操做以前 接收操做发生在相应的发送操做完成前 而发送操做则发送在打印以前
If the channel were buffered (e.g., c = make(chan int, 1)) then the program would not be guaranteed to print "hello, world". (It might print the empty string, crash, or do something else.)
若是是有缓冲的channel 那么上面那段代码就没法保证会打印“hello world”了
The sync package implements two lock data types, sync.Mutex and sync.RWMutex.
sync包实现了两种类型的锁 sync.Mutex和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.
对任意的sync.Mutex或者sync.RWMutex变量l n <m, 第N次调用l.Unlock 发生在 m次调用l.Lock返回前
This program:
var l sync.Mutex var a string func f() { a = "hello, world" l.Unlock() } func main() { l.Lock() go f() l.Lock() print(a) }
is guaranteed to print "hello, world". The first call to l.Unlock() (in f) happens before the second call to l.Lock() (in main) returns, which happens before the print.
上述代码能够保证输出“hello,world” 第一次调用l.Unlock 发生在第二次调用l.Lock以前 而其二次调用I.Lock发生在print操做以前
For any call to l.RLock on a sync.RWMutex variable l, there is an n such that the l.RLock happens (returns) after call n to l.Unlock and the matching l.RUnlock happens before call n+1 to l.Lock.
在sync.RWMutex变量l上的任何一次l.RLock操做 会存在一个n l.RLcok发生在调用第n次l.Unlock以后 对于的l.RUnlock发生在第n+1次l.Lock调用以前
The sync package provides a safe mechanism for initialization in the presence of multiple goroutines through the use of the Once type. Multiple threads can execute once.Do(f) for a particular f, but only one will run f(), and the other calls block until f() has returned.
sync包为在多goroutine环境下初始化提供了一种安全的机制 须要使用sync包的Once类型 多个线程能够同时执行once.Do(f) 可是只有一个线程会调用f() 其它的线程会阻塞 直到f()返回
A single call of f() from once.Do(f) happens (returns) before any call of once.Do(f) returns.
经过once.Do(f)调用f发生在任何其它的once.Do(f)返回以前
In this program:
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() }
calling twoprint causes "hello, world" to be printed twice. The first call to twoprint runs setup once.
调用两次twoprint会输出两次“hello,world”
Note that a read r may observe the value written by a write w that happens concurrently with r. Even if this occurs, it does not imply that reads happening after r will observe writes that happened before w.
注意 读操做r可能会观察到和r并行的写操做w 即便出现这样的状况 也不意味着r以后的读会观察到w以前的写操做
In this program:
var a, b int func f() { a = 1 b = 2 } func g() { print(b) print(a) } func main() { go f() g() }
it can happen that g prints 2 and then 0.
上面这段代码可能会输出2 而后 输出0
This fact invalidates a few common idioms. 这个事实让不少习觉得常的事情做废了
Double-checked locking is an attempt to avoid the overhead of synchronization. For example, the twoprint program might be incorrectly written as:
多检查一次锁 是避免同步问题的一种途径 例如: twoprint程序可能被写成下面这样:
var a string var done bool func setup() { a = "hello, world" done = true } func doprint() { if !done { once.Do(setup) } print(a) } func twoprint() { go doprint() go doprint() }
but there is no guarantee that, in doprint, observing the write to done implies observing the write to a. This version can (incorrectly) print an empty string instead of "hello, world".
可是 这样写并不能保证 doprint中观察到对done的写 就能观察到对a的写操做
Another incorrect idiom is busy waiting for a value, as in:
另外一种错误的用法是 忙等某一个变量值:
var a string var done bool func setup() { a = "hello, world" done = true } func main() { go setup() for !done { } print(a) }
As before, there is no guarantee that, in main, observing the write to done implies observing the write to a, so this program could print an empty string too. Worse, there is no guarantee that the write to done will ever be observed by main, since there are no synchronization events between the two threads. The loop in main is not guaranteed to finish.
这样写的结果和上面的状况同样 这段代码可能只会打印空字符串 更惨的是 并不能保证对done的写 会在main中被观察到 由于它们之间并无同步的事情 main中的loop并不能保证 能等到那个想要的值
There are subtler variants on this theme, such as this program.
也有一些上面代码的变种:
type T struct { msg string } var g *T func setup() { t := new(T) t.msg = "hello, world" g = t } func main() { go setup() for g == nil { } print(g.msg) }
Even if main observes g != nil and exits its loop, there is no guarantee that it will observe the initialized value for g.msg.
效果同样
In all these examples, the solution is the same: use explicit synchronization.
看的不太明白 哎。。。