[英]Understanding `memory_order_acquire` and `memory_order_release` in C++11
[英]C++11 memory_order_acquire and memory_order_release semantics?
http://en.cppreference.com/w/cpp/atomic/memory_order和其他 C++11 在線參考資料,將 memory_order_acquire 和 memory_order_release 定義為:
這似乎允許在獲取操作之前執行獲取后寫入,這對我來說也很奇怪(通常獲取/釋放操作語義限制所有內存操作的移動)。
相同的在線資源( http://en.cppreference.com/w/cpp/atomic/atomic_flag )表明可以使用 C++ 原子和上述寬松的內存排序規則構建自旋鎖互斥鎖:
lock mutex: while (lock.test_and_set(std::memory_order_acquire))
unlock mutex: lock.clear(std::memory_order_release);
有了這個鎖定/解鎖的定義,如果 memory_order_acquire/release 確實以這種方式定義(即,不禁止對 post-acquire-writes 重新排序),下面的簡單代碼不會被破壞:
Thread1:
(0) lock
(1) x = 1;
(2) if (x != 1) PANIC
(3) unlock
Thread2:
(4) lock
(5) x = 0;
(6) unlock
以下執行是否可能:(0)鎖定,(1)x = 1,(5)x = 0,(2)恐慌? 我錯過了什么?
自旋鎖互斥體實現對我來說看起來不錯。 我認為他們對獲取和釋放的定義完全錯誤。
這是我所知道的對獲取/釋放一致性模型的最清晰的解釋:Gharachorloo; 萊諾斯基; 勞登; 長臂猿; 古普塔; Hennessy:可擴展共享內存多處理器中的內存一致性和事件排序, Int'l Symp Comp Arch ,ISCA(17):15-26, 1990,doi 10.1145/325096.325102 。 (doi 在 ACM 付費專區后面。實際鏈接是指向不在付費專區后面的副本。)
查看第 3.3 節中的條件 3.1 和隨附的圖 3:
重點是:獲取和釋放是順序一致的1 (所有線程全局同意獲取和釋放發生的順序。)所有線程全局同意特定線程上的獲取和釋放之間發生的事情發生在獲取和釋放。 但是允許在發布之后(通過硬件或編譯器)將正常加載和存儲移動到發布上方,並且允許將獲取之前的正常加載和存儲移動(通過硬件或編譯器)到獲取之后.
(腳注 1:這對於大多數實現來說都是正確的,但通常對 ISO C++ 來說是誇大其詞。允許讀者線程不同意由 2 個其他線程完成的 2 個存儲的順序。請參閱4 個線程的獲取/釋放語義和此答案有關為 POWER CPU 編譯的 C++ 的詳細信息,它展示了釋放和獲取在實踐中的差異,而不是 seq_cst。但大多數 CPU 僅通過一致性緩存獲取內核之間的數據,這意味着全局順序確實存在。)
在C++ 標准中(我使用了 2012 年 1 月草案的鏈接),相關部分是 1.10(第 11 到 14 頁)。
發生在之前的定義旨在模仿Lamport; 分布式系統中的時間、時鍾和事件排序, CACM ,21(7):558-565,1978 年 7 月。 C++ acquiss對應Lamport的receives ,C++ releases對應Lamport的sends 。 蘭波特放置共訂單上的事件的序列中的單個線程,其中C ++具有允許不完整階內(參見第1.9節段13-15,第10頁的C ++的測序-前定義)。盡管如此,sequenced-在訂購之前幾乎是您所期望的。 語句按照它們在程序中給出的順序進行排序。 第 1.9 節,第 14 段:“與完整表達式相關的每個值計算和副作用在與要評估的下一個完整表達式相關的每個值計算和副作用之前排序。”
第 1.10 節的重點是說,一個無數據競爭的程序產生相同的明確定義的值,就好像程序在具有順序一致內存且沒有編譯器重新排序的機器上運行一樣。 如果存在數據競爭,則程序根本沒有定義的語義。 如果沒有數據競爭,則允許編譯器(或機器)對不會造成順序一致性錯覺的操作重新排序。
第 1.10 節第 21 段(第 14 頁)說:如果從不同的線程對對象 X 進行了一對訪問 A 和 B,那么程序不是無數據競爭的,其中至少有一個訪問有副作用,並且兩者都沒有A 在 B 之前發生,B 在 A 之前發生。否則程序是無數據競爭的。
第 6-20 段非常仔細地定義了happens-before 關系。 關鍵定義是第 12 段:
“在以下情況下,評估A 發生在評估 B之前:
因此,如果獲取在幾乎任何其他語句之前(在同一線程中)排序,那么獲取必須出現在該語句之前。 (包括該語句是否執行寫入。)
同樣:如果幾乎任何語句都在發布之前(在同一線程中)排序,那么該語句必須出現在發布之前。 (包括該語句是否僅執行值計算(讀取)。)
編譯器允許釋放之前,釋放后移至其他計算(或從acquire之前給acquire后)的原因是因為一個事實,即這些行動特別沒有一個線程間的關系之前發生(因為它們在臨界區之外)。 如果它們競爭語義是未定義的,並且如果它們不競爭(因為它們不共享),那么您無法准確判斷它們何時發生在同步方面。
這是一個很長的說法:cppreference.com 對獲取和釋放的定義是完全錯誤的。 您的示例程序沒有數據競爭條件,並且不會發生 PANIC。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.