最近我對 C 中記憶體順序的了解越多,它就越令人困惑。希望您能幫助我澄清這一點(出于純粹的理論目的)。假設我有以下代碼:
std::atomic<int> val = { 0 };
std::atomic<bool> f1 = { false };
std::atomic<bool> f2 = { false };
void thread_1() {
f1.store(true, std::memory_order_relaxed);
int v = 0;
while (!val.compare_exchange_weak(v, v | 1,
std::memory_order_release));
}
void thread_2() {
f2.store(true, std::memory_order_relaxed);
int v = 0;
while (!val.compare_exchange_weak(v, v | 2,
std::memory_order_release));
}
void thread_3() {
auto v = val.load(std::memory_order_acquire);
if (v & 1) assert(f1.load(std::memory_order_relaxed));
if (v & 2) assert(f2.load(std::memory_order_relaxed));
}
問題是:任何斷言都可以是假的嗎?一方面,cppreference聲稱,std::memory_order_release禁止在執行緒 1-2 中的交換之后對兩個存盤進行重新排序,并且std::memory_order_acquire在執行緒 3 中禁止在第一次加載之前對兩個讀取進行重新排序。因此,如果執行緒 3 看到第一個或第二個位集,這意味著存盤到相應的布林值已經發生并且它必須為真。
另一方面,執行緒 3 與釋放它從 獲取的值的人同步val。是否會發生這樣的情況(理論上如果不是在實踐中)執行緒 3 “獲取”了執行緒 2 的交換“1 -> 3”(因此 f2 load 回傳 true),而不是執行緒 1 的“0 -> 1” (因此第一個斷言觸發)?考慮到“重新排序”的理解,這種可能性對我來說毫無意義,但我找不到任何確認這不會在任何地方發生。
uj5u.com熱心網友回復:
由于 ISO C 的“發布順序”規則,這兩個斷言都不會失敗。 這是提供您假設必須存在于最后一段中的保證的形式主義。
唯一的存盤val是設定了適當位的釋放存盤,在相應的存盤到f1or之后完成f2。因此,如果thread_3看到一個設定了 1 位的值,它肯定已經與設定相應變數的寫入器同步。
至關重要的是,它們都是 RMW 的每個部分,因此形成了一個釋放序列,如果碰巧看到.thread_3val == 3
(即使是relaxedRMW 也可以是發布序列的一部分,盡管在這種情況下,對于寬松 RMW 之前的內容不會有發生之前的保證,僅適用于此原子變數上的此執行緒或其他執行緒的其他釋放操作。如果thread_2使用過mo_relaxed,斷言f2可能會失敗,但它仍然無法破壞事物,因此斷言f1可能會失敗。另請參閱“發布序列”是什么意思?和https://en.cppreference.com/w/cpp/原子/記憶體順序)
如果有幫助,我認為那些 CAS 回圈完全等同于val.fetch_or(1, release). 這絕對是編譯器在具有 CAS 但不是原子 OR 原語的機器上實作 fetch_or 的方式。IIRC,在 ISO C 模型中,CAS 故障只是一個負載,而不是 RMW。沒關系;寬松的無操作 RMW 仍會傳播發布序列。
(有趣的事實:x86 asmlock cmpxchg始終是一個真正的 RMW,即使在失敗時,至少在紙面上也是如此。但它也是一個完整的障礙,因此基本上與任何關于弱序 RMW 的推理無關。)
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/444265.html
上一篇:相同的數學和數字不同的答案?
下一篇:在遞回插入排序中出現分段錯誤
