請考慮以下同步問題:
initially:
version = 0 // atomic variable
data = 0 // normal variable (there could be many)
Thread A:
version
data = 3
Thread B:
d = data
v = version
assert(d != 3 || v == 1)
基本上,如果執行緒 B 看到了,data = 3那么它也必須看到version .
我們必須施加的最弱的記憶體順序和同步是什么,以便始終滿足執行緒 B 中的斷言?
如果我正確理解C memory_order,則不會執行發布-獲取順序,因為這保證version 了執行緒 A 中的 BEFORE 操作將被v = version執行緒 B 中的 AFTER 操作看到。
獲取和釋放柵欄也朝著相同的方向作業,但更通用。
正如我所說,我需要另一個方向: B seesdata = 3 意味著B sees version = 1。
我正在使用這種“版本控制方法”來盡可能避免我正在設計的資料結構中的鎖定。當我看到某些事情發生了變化時,我會退后一步,閱讀新的內容version,然后再試一次。
我試圖讓我的代碼盡可能地便攜,但我的目標是 x86-64 CPU。
uj5u.com熱心網友回復:
只要您的資料不包含指標,您可能正在尋找SeqLock 。(如果是這樣,那么您可能需要更像RCU的東西來保護可能加載指標、暫停/休眠一段時間,然后在很久以后取消參考該指標的讀取器。)
您可以使用 SeqLock 序列計數器作為版本號。(version = tmp_counter >> 1因為每次寫入有效負載需要兩次增量,以讓讀者在讀取非原子時檢測到撕裂data。并確保他們看到data與此序列號相符的內容。確保您沒有第三次讀取原子計數器; 使用您讀入的本地 tmp 在復制之前/之后驗證匹配data。)
如果讀者在修改時碰巧嘗試讀取,data則必須重試。但它是非原子的,因此如果執行緒 B 看到data = 3的執行緒不可能成為創建同步的一部分;它只能是您與作者提供的版本號同步的結果。
看:
用 32 位原子實作 64 位原子計數器——我在 C 中的 SeqLock 的嘗試,有很多評論。這有點像 hack,因為 ISO C 的資料競爭 UB 規則過于嚴格;SeqLock 依賴于檢測可能的撕裂而不使用撕裂的資料,而不是完全避免并發訪問。這在沒有硬體競爭檢測的機器上很好,因此不會出錯(就像所有真正的 CPU 一樣),但 C 仍然呼叫那個 UB,即使有
volatile(盡管這使它更多地進入實作定義的領域)。在實踐中它很好。GCC 使用 `memory_order_seq_cst` 跨負載重新排序。這是允許的嗎?- 8.1 中修復的 GCC 錯誤,可能會破壞 seqlock 實作。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/451290.html
