プログラミング言語C/C++が提供するatomic変数*1とそのコピー操作に関するメモ。
2個のatomic変数間における “atomicなコピー操作” は提供されない。通常は “atomic変数からの読込(load)”+“atomic変数への格納(store)” で代用すればよい。ただし、下記コードのような2つのコピー操作が並行実行された場合、load/store操作のインターリーブが起こりうることに留意。*2
// C++11 #include <atomic> using namespace std; // C11 #include <stdatomic.h> atomic_int x = ATOMIC_VAR_INIT(0); // x = 0 atomic_int y = ATOMIC_VAR_INIT(1); // y = 1 void thread1() { atomic_store( &x, atomic_load(&y) ); // x = y; } void thread2() { atomic_store( &y, atomic_load(&x) ); // y = x; } // 並行実行の結果として x==1 && y==0 となる可能性あり
2022-07-25追記:ATOMIC_VAR_INIT
マクロはC17で非推奨(deprecated)、C2x(C23)で削除される*3。C++20で非推奨(deprecated)*4、C++2a(C++23)またはC++2b(C++26)で削除される公算が大きい*5。
真に “atomicなコピー操作” が必要ならば、mutex等を用いた排他制御を行うこと。ただし、この場合atomic変数とする必然性はない。
// C++11 #include <mutex> int x = 0, y = 1; std::mutex m; void thread1() { std::lock_guard<decltype(m)> lk(m); x = y; } void thread2() { std::lock_guard<decltype(m)> lk(m); y = x; } // 実行結果は x==y==0 または x==y==1 のいずれか
C++11
#include <atomic> std::atomic<int> x; std::atomic<int> y = ATOMIC_VAR_INIT(42); x = y; // NG: ill-formed x.store(y.load()); // OK
std::atomic<T>クラステンプレートでは、コピーコンストラクタ/コピー代入演算子がdelete指定されている。JTC1/SC22/WG21 N3337 29.5/p3より引用。
3 Specializations and instantiations of the
atomic
template shall have a deleted copy constructor, a deleted copy assignment operator, and a constexpr value constructor.
C11
#include <stdatomic.h> _Atomic int x; _Atomic int y = ATOMIC_VAR_INIT(42); x = y; // OK: ただし下記コードと等価 // atomic_store(&x, atomic_load(&y));
代入式の右辺ではlvalue conversionにより、atomic型から非atomic型への変換(値のload)が行われる。JTC1/SC22/WG14 N1570 6.2.6.1/p9, 6.3.2.1/p2より一部引用。
9 Loads and stores of objects with atomic types are done with
memory_order_seq_cst
semantics.
2 (snip); this is called lvalue conversion. If the lvalue has qualified type, the value has the unqualified version of the type of the lvalue; additionally, if the lvalue has atomic type, the value has the non-atomic version of the type of the lvalue; otherwise, the value has the type of the lvalue. (snip)
関連URL
*1:C11:_Atomic T または _Atomic(T) または atomic_T,C++11:std::atomic<T> または std::atomic_T
*2:非 memory_order_seq_cst による弱いメモリ順序に起因する事象とは異なり、逐次一貫性(sequential consistency)実行の下でも生じる事象。
*3:https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2886.htm
*4:https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0883r2.pdf
*5:https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2139r2.html#3.24