簡體   English   中英

混合對同一個原子變量的放松訪問和獲取/釋放訪問如何影響同步?

[英]How does mixing relaxed and acquire/release accesses on the same atomic variable affect synchronises-with?

我對 C++ memory model 中同步關系的定義有疑問,當放松訪問和獲取/釋放訪問混合在同一個原子變量上時。 考慮以下由全局初始化程序和三個線程組成的示例:

int x = 0;
std::atomic<int> atm(0);

[thread T1]
x = 42;
atm.store(1, std::memory_order_release);

[thread T2]
if (atm.load(std::memory_order_relaxed) == 1)
    atm.store(2, std::memory_order_relaxed);

[thread T3]
int value = atm.load(std::memory_order_acquire);
assert(value != 1 || x == 42);  // Hopefully this is guaranteed to hold.
assert(value != 2 || x == 42);  // Does this assert hold necessarily??

我的問題是T3中的第二個斷言是否會在 C++ memory model 下失敗。請注意, 這個 SO 問題的答案表明,如果T2使用加載/獲取和存儲/釋放,則斷言不會失敗; 如果我弄錯了,請糾正我。 然而,如上所述,答案似乎取決於在這種情況下如何准確定義同步關系。 我被 cppreference 上的文字弄糊塗了,我想出了以下兩種可能的解讀。

  1. 第二個斷言失敗。 T1中存儲到atm在概念上可以理解為存儲1_release ,其中_release是指定值存儲方式的注解; 同樣, T2中的存儲可以理解為存儲2_relaxed 因此,如果T3中的加載返回2 ,線程實際上讀取2_relaxed 因此, T3中的加載與T1中的存儲同步,並且不能保證T3看到x == 42 但是,如果T3中的加載返回1 ,則讀取了1_release ,因此T3中的加載與T1中的存儲同步,並且T3保證看到x == 42

  2. 第二個斷言成功。 如果T3中的負載返回2 ,則此負載讀取T2中寬松存儲的副作用; 但是,僅當atm的修改順序包含具有釋放語義的先前存儲時, T2的此存儲才會出現在atm的修改順序中。 因此, T3中的加載/獲取與T1的存儲/釋放同步,因為后者在atm的修改順序中必然先於前者。

乍一看, 這個 SO 問題的答案似乎表明我的閱讀 1 是正確的。 然而,這個答案似乎有微妙的不同:答案中的所有存儲都是釋放的,問題的關鍵是看到加載/獲取和存儲/釋放在一對線程之間建立同步。 相比之下,我的問題是當 memory 訂單是異構時,如何准確定義 synchronises-with。

我實際上希望閱讀 2 是正確的,因為這會使關於並發的推理更容易。 線程T2不讀取或寫入除atm以外的任何 memory; 因此, T2本身沒有同步要求,因此應該能夠使用寬松的 memory 順序。 相比之下, T1發布xT3使用它——也就是說,這兩個線程相互通信,因此它們應該清楚地使用獲取/釋放語義。 換句話說,如果解釋 1 被證明是正確的,那么僅僅考慮T2做了什么就不能編寫代碼T2 相反, T2的代碼需要知道它不應該“干擾” T1T3之間的同步。

無論如何,了解在這種情況下標准究竟批准了什么對我來說似乎是絕對重要的。

因為您在 T2 中的單獨加載和存儲上使用了寬松的排序,所以釋放序列被破壞並且可以觸發第二個斷言(盡管不是在 X86 等 TSO 平台上)。
您可以通過在線程 T2 中使用 acq/rel 排序(如您建議的那樣)或修改 T2 以使用原子讀-修改-寫操作 (RMW) 來解決此問題,如下所示:

[Thread T2]
int ret;
do {
    int val = 1;
    ret = atm.compare_exchange_weak(val, 2, std::memory_order_relaxed);
} while (ret != 0);

atm的修改順序是 0-1-2 並且 T3 將在 1 或 2 上拾取並且任何斷言都不會失敗。

T2 的另一個有效實現是:

[thread T2]
if (atm.load(std::memory_order_relaxed) == 1)
{
    atm.exchange(2, std::memory_order_relaxed);
}

這里的RMW本身是無條件的,必須伴隨着if語句&(relaxed)load來保證atm的修改順序是0-1或者0-1-2
如果沒有 if 語句,修改順序可能是 0-2,這會導致斷言失敗。 (這是有效的,因為我們知道在程序的整個 rest 中只有一個其他寫入。單獨的if() / exchange當然通常等同於compare_exchange_strong 。)

在C++標准中,相關引用如下:

[介紹比賽]
以原子 object M 上的釋放操作 A 為首的釋放序列是 M 修改順序中副作用的最大連續子序列,其中第一個操作是 A,隨后的每個操作都是原子讀-修改-寫操作。

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

這個問題是關於為什么RMW 在發布序列中工作。

暫無
暫無

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

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