在無鎖 queue.pop() 中,我在與回圈內的原子獲取同步后讀取了一個 trivialy_copyable 變數(整數型別)。最小化偽代碼:
//somewhere else writePosition.store(...,release)
bool pop(size_t & returnValue){
writePosition = writePosition.load(aquire)
oldReadPosition = readPosition.load(relaxed)
size_t value{};
do{
value = data[oldReadPosition]
newReadPosition = oldReadPosition 1
}while(readPosition.compare_exchange(oldReadPosition, newReadPosition, relaxed)
// here we are owner of the value
returnValue = value;
return true;
}
data[oldReadPosition]只有當從另一個執行緒讀取此值時,才能更改記憶體。
讀寫位置是 ABA 安全的。用簡單的拷貝,value = data[oldReadPosition]記憶體data[oldReadPosition]不會變。
但是如果另一個執行緒已經讀取了 oldPosition 并更改了 readPosition,則寫入執行緒queue.push(...)可以在讀取時更改。data[oldReadPosition]
如果您使用該值,這將是一個競爭條件,但是當我們value保持不變時,它是否也是一個競爭條件,因此是未定義的行為?標準不夠具體,或者我不明白。imo,這應該是可能的,因為它沒有效果。我很高興得到一個合格的答案以獲得更深入的見解
多謝
uj5u.com熱心網友回復:
是的,它是 ISO C 中的 UB;value = data[oldReadPosition]在 C 抽象機中涉及讀取該物件的值。(通常這意味著左值到右值的轉換,IIRC。)
但它大多是無害的,可能只會在具有硬體競爭檢測的機器上出現問題(不是普通的主流 CPU,但可能在 C 實作上,比如帶有執行緒清理器的 clang)。
非原子讀取然后檢查可能的撕裂的另一個用例是 SeqLock,讀者可以通過在非原子讀取之前和之后從原子計數器讀取相同的值來證明沒有撕裂。它是 C 中的 UB,即使volatile對于非原子資料也是如此,盡管這可能有助于確保編譯器生成的 asm 是安全的。(通過現有編譯器的記憶體屏障和當前對原子的處理,即使是非易失性也可以使 asm 作業)。請參閱在固定不同 CPU 的 2 個執行緒之間傳遞一些變數的最佳方式
atomic_thread_fenceSeqLock 的安全仍然是必要的,并且原子負載的一些必要順序是 wrt。如果非原子不能與某些東西同步并創建一個發生之前,它可能是一個實作細節。
人們確實在現實生活中使用 Seq Locks,這取決于現實生活中的編譯器事實上定義了比 ISO C 更多的行為這一事實。或者另一種說法是,它現在正好起作用;如果您對在非原子讀取周圍放置的代碼很小心,那么編譯器不太可能做任何有問題的事情。
但是你肯定會冒險越過保證行為的安全區域,并且可能需要了解 C 如何編譯為 asm,以及 asm 如何在你關心的目標平臺上作業;另請參閱誰害怕一個糟糕的優化編譯器?在 LWN 上;它針對的是 Linux 內核代碼,它是手動原子和類似東西的主要用戶。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/429309.html
上一篇:有什么方法可以知道在發送之前將在TCP上發送多少位元組?
下一篇:可變大小的內置陣列?
