簡體   English   中英

為什么“獲取/釋放”不能保證 c++11 中的順序一致性?

[英]Why 'acquire/release' can not guarantee sequential consistency in c++11?

-Thread 1-                
y.store (20, memory_order_release); 
x.store (10, memory_order_release);

-Thread 2-
if (x.load(memory_order_acquire) == 10) {
  assert (y.load(memory_order_acquire) == 20);
  y.store (10, memory_order_release)
}

-Thread 3-
if (y.load(memory_order_acquire) == 10) {
  assert (x.load(memory_order_acquire) == 10);
}

GCC Atomic Wiki段落“Overall Summary”說,上面的代碼assert(x.load(memory_order_acquire))可能會失敗 但我不明白為什么?

我的理解是:

  1. 由於獲取障礙,線程 3 無法LoadLoad重新排序。
  2. 由於釋放障礙,線程 1 無法對StoreStore重新排序。
  3. 當 Thread2 read(x)->10 時,x 必須從 storebuffer 刷新到 Thread1 中的緩存,所以每個線程都知道 x 的值發生了變化,例如使緩存行無效。
  4. Thread3 使用Acquire屏障,所以它可以看到 x(10)。

這是一個糟糕的例子,盡管我想它確實說明了放松的原子是多么令人費解。

[介紹.執行]p9:

與完整表達式相關的每個值計算和副作用在與要評估的下一個完整表達式相關的每個值計算和副作用之前排序。

[atomics.order]p2:

對原子對象M執行釋放操作的原子操作A與對M執行獲取操作的原子操作B同步,並從以A為首的釋放序列中的任何副作用中獲取其值。

因此,顯示的評估通過先序和同步關系鏈接在一起:

Thread 1                   Thread 2              Thread 3

y.store(20)
   |
   | s.b.
   V           s.w.
x.store(10)  -------->  x.load() == 10
                               |
                               | s.b.
                               V      s.w.
                        y.store(10) --------> y.load() == 10
                                                  |
                                                  | s.b.
                                                  V
                                              x.load() == ?

因此,鏈中的每個評估都發生在下一個之前(參見 [intro.races]p9-10)。

[介紹.races]p15,

如果一個原子對象M的值計算A M的值計算B之前發生,並且AM上的副作用X取它的值,然后用B中計算出的值應要么是或者存儲在由X所存儲的值的值由M上,副作用ÿ其中Y如下XM的修改次序。

這里, A是線程 2 中取值為 10 的負載, B是線程 3 中的負載(在斷言中)。 由於A發生在B之前,並且對x沒有其他副作用,因此B也必須讀取 10。


Herb Sutter 在他的博客上有一個更簡單的例子:

T1: x = 1;
T2: y = 1;
T3: if( x == 1 && y == 0 ) puts("x first");
T4: if( y == 1 && x == 0 ) puts("y first");

您絕對需要順序一致性來保證最多打印一行。

您的示例是多線程程序的特例,它僅使用原子對象進行同步,而不是用於傳遞具有顯着熵的信息:寫入的唯一值充當里程碑

這意味着任何商店:

  • 是一個釋放操作
  • 傳達一個值,該值指示一個線程的進度中的精確點

表單必須正好是A.store (C, memory_order_release); 在哪里:

  • A是原子對象
  • C是常數

和對(A,C)唯一地表征程序行。

相反,每個負載:

  • 是一個獲得
  • 只檢查特定值

表格必須完全正確

if (A.load(memory_order_acquire) == C) { ... }

沒有 else 子句的地方。

讀取特定值指示進度並確定所有先前的副作用(在特定程序點之前)已經發生。 這一小類程序從來沒有有趣的多線程行為,因為一切都是進程的函數:里程碑被傳遞,行為必須是純順序的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM