所以我正在阅读有关即将推出的 C++0x 标准的一部分的内存模型。但是,我对允许编译器执行的某些限制感到有些困惑,特别是关于推测加载和存储的限制。
首先,一些相关的东西:
Hans Boehm 关于线程和 C++0x 中的内存模型的页面
Sutter,“棱镜:微软本机代码平台的基于原则的顺序内存模型”,N2197
现在,基本思想本质上是“无数据争用程序的顺序一致性”,这似乎是在易于编程和允许编译器和硬件机会优化之间的一个不错的折衷。如果不同线程对同一内存位置的两次访问没有排序,则定义为发生数据竞争,其中至少一次存储到内存位置,并且至少一次不是同步操作。这意味着对共享数据的所有读/写访问都必须通过某种同步机制,例如互斥锁或对原子变量的操作(好吧,可以仅对专家以宽松的内存顺序对原子变量进行操作,但默认提供为了顺序一致性)。
鉴于此,我对普通共享变量上的虚假或推测加载/存储的限制感到困惑。例如,在 N2338 中,我们有示例
switch (y) {
case 0: x = 17; w = 1; break;
case 1: x = 17; w = 3; break;
case 2: w = 9; break;
case 3: x = 17; w = 1; break;
case 4: x = 17; w = 3; break;
case 5: x = 17; w = 9; break;
default: x = 17; w = 42; break;
}
不允许编译器转换成
tmp = x; x = 17;
switch (y) {
case 0: w = 1; break;
case 1: w = 3; break;
case 2: x = tmp; w = 9; break;
case 3: w = 1; break;
case 4: w = 3; break;
case 5: w = 9; break;
default: w = 42; break;
}
因为如果 y == 2 存在对 x 的虚假写入,如果另一个线程同时更新 x,这可能是一个问题。但是,为什么这是一个问题?这是一场数据竞赛,无论如何都是被禁止的;在这种情况下,编译器通过两次写入 x 只会使情况变得更糟,但即使是一次写入也足以进行数据竞争,不是吗?即一个合适的 C++0x 程序需要同步对 x 的访问,在这种情况下将不再存在数据竞争,并且虚假存储也不会成为问题?
I'm similarly confused about Example 3.1.3 in N2197 and some of the other examples as well, but maybe an explanation for the above issue would explain that too.
EDIT: The Answer:
The reason why speculative stores are a problem is that in the switch statement example above, the programmer might have elected to conditionally acquire the lock protecting x only if y != 2. Hence the speculative store might introduce a data race that was not there in the original code, and the transformation is thus forbidden. The same argument applies to Example 3.1.3 in N2197 as well.