最低订购要求
A Minimum Ordering Requirement
设x
和y
为std::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.load
和y.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
- 开头的一家商店
- 最后一个负载
因此获取和释放语义都不会造成任何差异。
设x
和y
为std::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.load
和y.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
- 开头的一家商店
- 最后一个负载
因此获取和释放语义都不会造成任何差异。