go mem
一个例子
思考这段代码输出什么?
package main
var a, b int
func f() {
a = 1
b = 2
}
func g() {
println(a)
println(b)
}
func main() {
go f()
g()
}
实际输出
0
0
这段代码的输出其实不固定,简单来讲就是
软件(编译器)或者硬件(CPU)系统可以根据对代码的分析结果,在一定程度上打乱代码的执行顺序,提高CPU的利用率
Go内存模型两个问题
- Happens Before
- 可见性
To serialize access, protect the data with channel operations or other synchronization primitives such as those in the sync and sync/atomic packages.
Happens 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.
编译器重排
X = 0
for i in range(100):
X = 1
print X
自动优化
X = 1
for i in range(100):
print X
但是问题是如果我们两个goroutine运行,导致X = 0就会出现问题了,所以通过多个goroutine访问数据,需要序列化访问。
happens before定义
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.
机器字
Reads and writes of values larger than a single machine word behave as multiple machine-word-sized operations in an unspecified order.
32 位系统和 64 位的系统,cpu 在执行一条指令的时候对于单个机器字长的的数据的写入可以保证是原子的,对于 32 位的就是 4 个字节,对于 64 位的就是 8 个字节,对于在 32 位情况下去写入一个 8 字节的数据时就需要执行两次写入操作,这两次操作之间就没有原子性,那就可能出现先写入后半部分的数据再写入前半部分,或者是写入了一半数据然后写入失败的情况。
同步
初始化
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的创建
The go statement that starts a new goroutine happens before the goroutine’s execution begins.
goroutine的销毁
The exit of a goroutine is not guaranteed to happen before any event in the progra.
内存重排
- C1 执行 a = 1 将 store buffer 中 a 的值写为 1
- C1 执行 b = 2 将 store buffer 中 b 的值写为 2, 然后由于某种原因将 b 的值写入到了内存中
- C2 去读取 b 的值,发现缓存中没有,就去内存中读,这时候 print 出来 2
- C2 去读取 a 的值,发现缓存中没有,就去内存中读,这时候 print 出来 0
ref
http://lailin.xyz/post/go-training-week3-go-memory-model.html