最低订购要求

A Minimum Ordering Requirement

xystd::atomic<int>类型的两个不同变量,并假设它们的当前值为1。什么是最宽松 的一组排序要求,所以下面的代码会生成一些输出? (即 order1 ... order4 应该使用什么?)

// Thread 1
x.store(0, order1);       // A
if(0 == y.load(order2))   // B
  std::cout << '1';       // C
// Thread 2
y.store(0, order3);       // E
if(0 == x.load(order4))   // F
  std::cout << '2';       // G

#StoreLoad 重新排序会导致两次加载都达到 return 旧值,因此您需要对所有 4 个操作完全顺序一致以防止这种情况发生。

阅读存储缓冲区以了解导致此类重新排序的原因。

您需要所有操作的顺序一致性。

Release/acquire 实际上并没有在这里强加任何顺序,因为在 release-store 之前没有 store 或在 acquire-load 之后没有加载,它们将被它排序。

任何商店的宽松内存排序,例如x.store 可能会导致商店在两个线程中以不同的顺序可见。然后它不违反剩余操作的顺序排序,使松弛存储在另一个线程加载后可见,而另一个存储在其相应加载后仍按顺序一致的总顺序排序。

通过对所有操作的顺序排序,输出是有保证的,因为在所有线程中必须以相同的方式观察到顺序操作的总顺序。此顺序必须与线程中的排序顺序一致,即x.store < y.loady.store < x.load。唯一的可能性是:

x.store < y.load  < y.store < x.load
x.store < y.store < x.load  < y.load
x.store < y.store < y.load  < x.load

y.store < x.load  < x.store < y.load
y.store < x.store < y.load  < x.load
y.store < x.store < x.load  < y.load

所有这些观察变量之一在存储后加载,触发 cout 语句之一。

如果例如x.store 不是 memory_order_seq_cst,那么,虽然它仍然需要在线程 1 中排在 y.load 之前,但在线程 2 中它可能在 x.load 之后变得可见。

y.load < y.store < x.load 仍然会满足其他操作的顺序一致性,并且 x 的修改顺序无论如何都可以满足。

两条简单的规则:

  • 作为并行性开始(线程开始)后的第一个动作,释放栅栏或操作没有任何可见的东西(在其他线程中),所以什么都没有 "released" 并且释放是无用(所有内容以前都是可见的,并且在同一时间启动的线程同样可见);
  • 作为并行性结束(线程结束)之前的最后一个动作,获取栅栏或操作没有任何东西可以使(在该线程中)可见,而在公共父线程中是不可见的

这里有:

x.store(0, order1);       // A
if(0 == y.load(order2))   // B
  • 开头的一家商店
  • 最后一个负载

因此获取和释放语义都不会造成任何差异。