内存模型的目的是为了定义清楚变量的读写在不同执行体里的可见性
Go的并发模型是基于CSP(Communicating Sequential Process)的,不同的Goroutine通过一种叫Channel的数据结构来通信;Java的并发模型则基于多线程和共享内存,有较多的概念(violatie, lock, final, construct, thread, atomic等)和场景,当然java.util.concurrent并发工具包大大简化了Java并发编程。
happen before
Happens-before用来指明Go程序里的内存操作的局部顺序。如果一个内存操作事件e1 happens-before e2,则e2 happens-after e1也成立;如果e1不是happens-before e2,也不是happens-after e2,则e1和e2是并发的。
在这个定义之下,如果以下情况满足,则对变量(v)的内存写操作(w)对一个内存读操作(r)来说允许可见的:
- r不在w开始之前发生(可以是之后或并发);
- w和r之间没有另一个写操作(w’)发生;
为了保证对变量(v)的一个特定写操作(w)对一个读操作(r)可见,就需要确保w是r唯一允许的写操作,于是如果以下情况满足,则对变量(v)的内存写操作(w)对一个内存读操作(r)来说保证可见的:
- w在r开始之前发生;
- 所有其它对v的写操作只在w之前或r之后发生;
可以看出后一种约定情况比前一种更严格,这种情况要求没有w或r没有其他的并发写操作
从本质上来讲,happens-before规则确定了CPU缓冲和主存的同步时间点(通过内存屏障等指令),从而使得对变量的读写顺序可被确定–也就是我们通常说的“同步”。
在Go编程中,Channel是被推荐的执行体间通信的方法,Go的编译器和运行态都会尽力对其优化。
- 对一个Channel的发送操作(send) happens-before 相应Channel的接收操作完成
- 关闭一个Channel happens-before 从该Channel接收到最后的返回值0
- 不带缓冲的Channel的接收操作(receive) happens-before 相应Channel的发送操作完成
sync.Once
仅一次的调用