アトミック::ロードがメモリバリアとしても機能し、以前のすべての非アトミック書き込みが他のスレッドから見えるようになると仮定するのは間違っていますか?
説明する:
volatile bool arm1 = false;
std::atomic_bool arm2 = false;
bool triggered = false;
スレッド 1:
arm1 = true;
//std::std::atomic_thread_fence(std::memory_order_seq_cst); // this would do the trick
if (arm2.load())
triggered = true;
スレッド 2:
arm2.store(true);
if (arm1)
triggered = true;
両方の「トリガー」を実行した後、真になると予想しました。arm1 をアトミックにすることを提案しないでください。要点は、atomic::load の動作を調査することです。
私はメモリ順序のさまざまな緩和されたセマンティクスの正式な定義を完全には理解していないことを認めなければなりませんが、順次一貫性のある順序付けは、「すべてのスレッドがすべての変更を観察する単一の全体的な順序が存在する」ことを保証するという点で非常に簡単だと思いました同じ順番で。」これは、デフォルトのメモリ順序 std::memory_order_seq_cst を持つ std::atomic::load がメモリ フェンスとしても機能することを意味します。これは、「順次一貫性のある順序付け」の下にある次のステートメントによってさらに裏付けられます。
すべてのマルチコア システムで完全な順次順序付けを行うには、完全なメモリ フェンス CPU 命令が必要です。
しかし、以下の簡単な例は、MSVC 2013、gcc 4.9 (x86)、および clang 3.5.1 (x86) では、アトミック ロードが単純にロード命令に変換される場合に当てはまらないことを示しています。
#include <atomic>
std::atomic_long al;
#ifdef _WIN32
__declspec(noinline)
#else
__attribute__((noinline))
#endif
long load() {
return al.load(std::memory_order_seq_cst);
}
int main(int argc, char* argv[]) {
long r = load();
}
gcc では次のようになります。
load():
mov rax, QWORD PTR al[rip] ; <--- plain load here, no fence or xchg
ret
main:
call load()
xor eax, eax
ret
基本的に同じである msvc と clang は省略します。ARM の gcc では、期待どおりの結果が得られます。
load():
dmb sy ; <---- data memory barrier here
movw r3, #:lower16:.LANCHOR0
movt r3, #:upper16:.LANCHOR0
ldr r0, [r3]
dmb sy ; <----- and here
bx lr
main:
push {r3, lr}
bl load()
movs r0, #0
pop {r3, pc}
これは学術的な質問ではありません。コード内で微妙な競合状態が発生し、std::atomic の動作に関する私の理解が疑問視されました。