簡體   English   中英

是防止部分讀取原子存儲所必需的“memory_order_relaxed”

[英]is `memory_order_relaxed` necessary to prevent partial reads of atomic stores

假設線程 1 正在使用memory_order_release (或任何其他順序)對變量v進行原子存儲,而線程 2 正在使用memory_order_relaxedv進行原子讀取。

在這種情況下,應該不可能進行部分讀取。 部分讀取的示例是從最新值讀取v的前半部分,從舊值讀取v的后半部分。

  1. 如果線程 2 現在讀取v而不使用原子操作,理論上我們可以進行部分讀取嗎?
  2. 我們可以在實踐中進行部分閱讀嗎? (問是因為我認為這對大多數處理器來說都無關緊要,但我不確定。)

對於 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM