std::atomic_compare_exchange_weak, std::atomic_compare_exchange_strong, std::atomic_compare_exchange_weak_explicit, std::atomic_compare_exchange_strong_explicit
| ヘッダ <atomic> で定義
|
||
| (1) | (C++11以上) | |
template< class T > bool atomic_compare_exchange_weak( std::atomic<T>* obj, typename std::atomic<T>::value_type* expected, typename std::atomic<T>::value_type desired ) noexcept; |
||
template< class T > bool atomic_compare_exchange_weak( volatile std::atomic<T>* obj, typename std::atomic<T>::value_type* expected, typename std::atomic<T>::value_type desired ) noexcept; |
||
| (2) | (C++11以上) | |
template< class T > bool atomic_compare_exchange_strong( std::atomic<T>* obj, typename std::atomic<T>::value_type* expected, typename std::atomic<T>::value_type desired ) noexcept; |
||
template< class T > bool atomic_compare_exchange_strong( volatile std::atomic<T>* obj, typename std::atomic<T>::value_type* expected, typename std::atomic<T>::value_type desired ) noexcept; |
||
| (3) | (C++11以上) | |
template< class T > bool atomic_compare_exchange_weak_explicit( std::atomic<T>* obj, typename std::atomic<T>::value_type* expected, typename std::atomic<T>::value_type desired, std::memory_order succ, std::memory_order fail ) noexcept; |
||
template< class T > bool atomic_compare_exchange_weak_explicit( volatile std::atomic<T>* obj, typename std::atomic<T>::value_type* expected, typename std::atomic<T>::value_type desired, std::memory_order succ, std::memory_order fail ) noexcept; |
||
| (4) | (C++11以上) | |
template< class T > bool atomic_compare_exchange_strong_explicit( std::atomic<T>* obj, typename std::atomic<T>::value_type* expected, typename std::atomic<T>::value_type desired, std::memory_order succ, std::memory_order fail ) noexcept; |
||
template< class T > bool atomic_compare_exchange_strong_explicit( volatile std::atomic<T>* obj, typename std::atomic<T>::value_type* expected, typename std::atomic<T>::value_type desired, std::memory_order succ, std::memory_order fail ) noexcept; |
||
*this と expected のオブジェクト表現 (C++20未満)値表現 (C++20以上)を比較し、それらがビット単位で等しければ、 *this を desired で置き換えます (読み込み-変更-書き込み操作を行います)。 そうでなければ、 obj の指す実際の値を *expected に読み込みます (ロード操作を行います)。
読み込み-変更-書き込み操作およびロード操作に対するメモリモデルはそれぞれ succ および fail です。 (1-2) のバージョンではデフォルトで std::memory_order_seq_cst が使用されます。
これらは std::atomic のメンバ関数を用いて定義されます。
obj->compare_exchange_weak(*expected, desired)obj->compare_exchange_strong(*expected, desired)obj->compare_exchange_weak(*expected, desired, succ, fail)obj->compare_exchange_strong(*expected, desired, succ, fail)引数
| obj | - | 比較および変更するアトミックオブジェクトを指すポインタ |
| expected | - | アトミックオブジェクトに格納されていることが期待される値を指す参照 |
| desired | - | 期待通りの場合にアトミックオブジェクトに格納する値 |
| succ | - | 比較が成功した場合の読み込み-変更-書き込み操作に対するメモリ同期順序付け。 すべての値を指定できます。 |
| fail | - | 比較が失敗した場合のロード操作に対するメモリ同期順序付け。 std::memory_order_release および std::memory_order_acq_rel は指定できません。 また、 succ より強い順序付けを指定することもできません。 (C++17未満)
|
戻り値
比較の結果。 *obj が *expected と等しければ true、そうでなければ false。
ノート
比較およびコピーはビット単位です (std::memcmp および std::memcpy と同様です)。 コンストラクタ、代入演算子、比較演算子は使用されません。
weak 版の関数 ((1) および (3)) は、スプリアスに失敗することが許されています。 つまり、等しい場合でも *obj != *expected であるかのように動作することがあります。 compare-and-exchange がループ内にある場合、プラットフォームによっては weak 版の方が良い性能を発揮します。
weak 版の compare-and-exchange ではループが必要だけれども strong 版ではそうでないというときは、 T のオブジェクト表現にパディングビットや (C++20未満)トラップビットが含まれていたり、同じ値に対して複数のオブジェクト表現を提供する (浮動小数点の NaN など) 可能性がなければ、 strong 版の方が好まれます。 それらの可能性がある場合は、一般的に weak 版の compare-and-exchange の方が、何らかの安定したオブジェクト表現に急速に収束するため、上手くいきます。
いくつかのメンバの値表現に寄与するけれども他のメンバの値表現には寄与しないビットを持つ共用体の場合、アクティブなメンバの値表現に寄与しないそのようなパディングビットが不定値を持つせいで compare-and-exchange が必ず失敗することがあります。
|
オブジェクトの値表現に決して寄与しないパディングビットは無視されます。 |
(C++20以上) |
例
compare and exchange 操作は、ロックフリーデータ構造の基本的な構成要素として、よく使われます。
#include <atomic>
template<class T>
struct node
{
T data;
node* next;
node(const T& data) : data(data), next(nullptr) {}
};
template<class T>
class stack
{
std::atomic<node<T>*> head;
public:
void push(const T& data)
{
node<T>* new_node = new node<T>(data);
// put the current value of head into new_node->next
new_node->next = head.load(std::memory_order_relaxed);
// now make new_node the new head, but if the head
// is no longer what's stored in new_node->next
// (some other thread must have inserted a node just now)
// then put that new head into new_node->next and try again
while(!std::atomic_compare_exchange_weak_explicit(
&head,
&new_node->next,
new_node,
std::memory_order_release,
std::memory_order_relaxed))
; // the body of the loop is empty
// note: the above loop is not thread-safe in at least
// GCC prior to 4.8.3 (bug 60272), clang prior to 2014-05-05 (bug 18899)
// MSVC prior to 2014-03-17 (bug 819819). See member function version for workaround
}
};
int main()
{
stack<int> s;
s.push(1);
s.push(2);
s.push(3);
}
欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
| DR | 適用先 | 発行時の動作 | 正しい動作 |
|---|---|---|---|
| P0558R1 | C++11 | exact type match required because T is deduced from multiple arguments
|
T is deduced from the atomic argument only
|
関連項目
| アトミックに、アトミックオブジェクトの値を非アトミック引数と比較し、等しければ交換を行い、等しくなければ読み込みます ( std::atomic<T>のパブリックメンバ関数)
| |
(C++11)(C++11) |
アトミックオブジェクトの値を非アトミック引数でアトミックに置き換え、そのアトミックの古い値を返します (関数テンプレート) |
| アトミック操作の std::shared_ptr に対する特殊化 (関数テンプレート) | |
atomic_compare_exchange, atomic_compare_exchange_explicit の C言語リファレンス
| |