C++11 内存模型:为什么编译器不能在优化期间跨 load() 操作移动语句?

C++11 memory model: why can't compiler move statements across load() operations during optimization?

据我了解,对于顺序一致和获取释放内存模型,如果一个线程的某些 x.store(some_value) 操作与另一个线程的 x.load() 操作同步,则:

规则 1. 所有发生在 x.store(some_value) 操作之前的内存操作必须出现在 x.load() 操作之前。

这一点我觉得很清楚:

-Thread 1-        
 y = 1;            
 x.store(2, memory_order_release);         

-Thread 2-
 if (x.load(memory_order_acquire) == 2)
   assert(y == 1)

如果编译器在 x.store(2) 之后放置 y = 1 操作,assert 可能会失败,这不是我们期望的行为。

规则2.所有发生在x.load()操作之后的内存操作也必须出现在x.store(some_value)操作之后。

但现在我很困惑:为什么编译器不能在 load() 操作之间移动语句?它怎么会违反程序的预期行为呢?我想到一个这样的例子:

-Thread 1-        
 x.store(2, memory_order_release); // x == 0 initially       

-Thread 2-
 while (x.load(memory_order_acquire) == 1);
 y = 2; // y == 0 initially

这里,如果编译器把y = 2操作放在while()之前,y就变成了2(否则死循环,y保持不变) ,但这个例子似乎是强制性的(我什至不确定编译器是否可以这样做 "optimization"),我怀疑规则 2 是针对更现实的情况创建的。

能否请您解释一下规则 2 的必要性?

如果加载之后的操作可以移动到加载之前,线程 2 中的 assert(y == 1) 可能会在线程 1 中的 y = 1 之前发生,即使(根据规则 1)分配发生在商店之前。只有两条规则一起才能保证两条语句按照正确的顺序执行。