[英]is `memory_order_relaxed` necessary to prevent partial reads of atomic stores
假設線程 1 正在使用memory_order_release
(或任何其他順序)對變量v
進行原子存儲,而線程 2 正在使用memory_order_relaxed
對v
進行原子讀取。
在這種情況下,應該不可能進行部分讀取。 部分讀取的示例是從最新值讀取v
的前半部分,從舊值讀取v
的后半部分。
v
而不使用原子操作,理論上我們可以進行部分讀取嗎?對於 1. 你如何建議這樣做?
atomic<T> v
是一個模板,它將T()
隱式轉換重載為.load(mo_seq_cst)
。 這使得撕裂變得不可能。 seq_cst atomic 就像輕松加上一些排序保證。
該模板還重載了像++
這樣的運算符來執行原子.fetch_add(1, mo_seq_cst)
。 (或者對於預增量,1+fetch_add 以產生已經增加的值)。
當然,如果您通過使用非原子char*
讀取它來查看atomic<T>
的對象表示的字節(例如使用memcpy(&tmp, &v, sizeof(int))
,如果另一個線程正在修改,那就是 UB它。是的,根據你的做法,你可能會在實踐中流淚。
對象太大而無法無鎖的可能性更大,但在某些實現中可能是這樣,例如對於 32 位系統上的 8 字節對象,它可以通過特殊指令實現 8 字節原子性,但通常只使用兩個 32 位加載.
例如,32 位 x86 可以使用 SSE 完成 8 字節的原子加載,然后將其反彈回 integer regs。 或lock cmpxchg8b
。 當編譯器只需要兩個 integer 寄存器時,他們不會這樣做。
但是許多提供原子 8 字節加載的 32 位 RISC 具有雙寄存器加載,從一條指令產生 2 個 output 寄存器。 例如 ARM ldrd
或 MIPS ld
。 編譯器確實使用這些來優化對齊的 8 字節加載,即使原子性不是目標,所以你可能會“走運”並且無論如何都不會看到撕裂。
無論如何,小對象通常恰好是原子的。 請參閱為什么 integer 在 x86 上的自然對齊變量原子上賦值?
當然,非原子訪問不會假設值可以異步更改,因此循環可以無限期地使用陳舊的值。 不像一個寬松的原子,它在當前的編譯器上就像volatile
,因為它總是重新訪問 memory。 (當然是通過一致的硬件緩存,只是不將值保存在寄存器中。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.