本頁詳細討論了 CAS 回圈:https : //preshing.com/20150402/you-can-do-any-kind-of-atomic-read-modify-write-operation/
fetch_multiplyC 中的一個示例:
uint32_t fetch_multiply(std::atomic<uint32_t>& shared, uint32_t multiplier){
uint32_t oldValue = shared.load();
while (!shared.compare_exchange_weak(oldValue, oldValue * multiplier)){}
return oldValue;
}
本質上,如果*memory值與我們的 oldValue 匹配,則 anewValue會自動存盤,否則 oldValue 會更新為*memory.
我有兩個問題:
1 - 為什么我們必須檢查oldValue記憶體中是否仍然沒有變化?如果我們只是將 newValue 寫入記憶體會發生什么?我們是否試圖避免覆寫或使用來自另一個執行緒的中間值?
2-假設這個場景有 2 個執行緒:
- 執行緒 B 試圖以非原子方式存盤未對齊的值。發生存盤撕裂。
- 執行緒 A 嘗試交換。
- 交換失敗,因為 oldValue 不匹配。記憶體中的中間(撕裂)值被加載到我們的 oldValue。
- 執行緒 A 與一個中間值相乘并嘗試另一個成功的交換。
- 現在執行緒 B 將其剩余的值寫入相同的位置,部分覆寫我們之前的寫入。
我假設Thread B可以以這么多的延遲運行,如果是這樣,我們不僅乘以一個中間值,它甚至還被部分覆寫,而 CAS 什么也沒做。
uj5u.com熱心網友回復:
我設法說服自己代碼是錯誤的。我認為它應該是這樣的:
uint32_t fetch_multiply(std::atomic<uint32_t>& shared, uint32_t multiplier){
uint32_t oldValue;
do {
oldValue = shared.load();
} while (!shared.compare_exchange_weak(oldValue, oldValue * multiplier));
return oldValue;
}
如果目標值更改為其他值,我們需要再次讀取 oldValue ,否則我們將永遠旋轉。
但是,CAS 構造的要點是您永遠無法在共享位置觀察到中間值。眼淚是不可能的;shared.load()防止它。這是在硬體中實作的。
“如果我們只是將 newValue 寫入記憶體會發生什么?” 那么你沒有原子訪問權限。始終遵循模式。
“非對齊值”如果shared是非對齊的,您甚至在談論std::atomic. 非對齊指標不能安全地取消參考。對于正常情況,*您只是依賴于位元組可尋址架構,但這是一個std::atomic. 如果它沒有對齊,即使在 x86 上也可能出現故障。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/407122.html
標籤:
