写这篇文章的原因很简单,公司内部的 Golang 社区组织了第一期分享,主讲嘉宾就是我们敬爱的曹大。这个必定是要去听的,只是曹大的讲题非常硬核,所以提前找他要了参考资料,花了 1 个小时提前预习,才不至于在正式分享的时候什么也不懂。当然了,这也是对自己和主讲者的尊重。所有的参考资料都在文章最后一部分,欢迎自行探索。
在我读曹大给我的中英文参考资料时,我发现英文的我能读懂,读中文却很费劲。经过对比,我发现,英文文章是由一个例子引入,循序渐进,逐步深入。跟着作者的脚步探索,非常有意思。而中文的博客上来就直奔主题,对于第一次接触的人非常不友好。
两者就像演绎法和归纳法区别。国内的教材通常是演绎法,也就是上来先讲各种概念、原理,再推出另一些定理,比较枯燥;国外的教材更喜欢由例子引入,步步深入,引人入胜。这里,不去评判孰孰劣。多看看一些英文原版材料,总是有益的。据我所知,曹大经常从亚马逊上购买英文书籍,这个侧面也可以反映曹大的水平高啊。据说英文书一般都很贵,可见曹大也是很有钱的。
所以啊,技术文章写好不容易,我也自省一下。
话不多说,贴上文章的目录:
什么是内存重排
分两种,硬件和软件层面的,包括 CPU 重排、编译器重排。
CPU 重排
引用参考资料 【内存一致模型】
里的例子:
在两个线程里同时执行上面的代码,A 和 B 初始化值都是 0,那最终的输出是什么?
先说几种显而易见的结果:
执行顺序 | 输出结果 |
---|---|
1-2-3-4 | 01 |
3-4-1-2 | 01 |
1-3-2-4 | 11 |
1-3-4-2 | 11 |
从 01 的排列组合来看,总共有4种:00、01、10、11。表格中还差两种:10、00。我们来重点分析下这两种结果究竟会不会出现。
首先是 10
,假设 (2) 输出 1,(4) 输出 0。那么首先给 2,3 排个序:(3) -> (2),因为先要将 B 赋值为 1,(2) 才能打印出 1;同理,(4) -> (1)。另外,因为先打印 1,所以 (2) 要在 (4) 前面,合起来:(3) -> (2) -> (4) -> (1)。(2) 竟然在 (1) 前面执行了,不可能的!
那我们再分析下 00
,要想打印 00,打印语句必须在相应变量赋值前执行:
图中箭头表示先后顺序。这就尴