std::atomic_thread_fence
| ヘッダー <atomic> で定義 |
||
| extern "C" void atomic_thread_fence( std::memory_order order ) noexcept; |
(C++11以降) | |
指定された order に従って、アトミック操作と関連付けられていない非アトミックアクセスおよび緩和されたアトミックアクセスの メモリ同期順序 を確立します。ただし、下記で説明するように、同期を設定するには少なくとも1つのアトミック操作が必要です。
目次 |
[編集] フェンス-アトミック同期
スレッドAにおけるリリースフェンスFは、スレッドBにおけるアトミックな アquire操作 Yと同期します。ただし、以下の条件を満たす場合です。
- アトミックストアX(任意のメモリ順序)が存在する。
- YはXによって書き込まれた値(または、Xがリリース操作であった場合、Xが先行するリリースシーケンス によって書き込まれるであろう値)を読み取る。
- スレッドAにおいて、FはXより順序付けられている。
この場合、スレッドAにおいてFよりも順序付けられているすべての非アトミックおよび緩和されたアトミックストアは、スレッドBにおいてYの後に同じ場所に対して行われるすべての非アトミックおよび緩和されたアトミックロードよりも 先に発生 します。
[編集] アトミック-フェンス同期
スレッドAにおけるアトミックな リリース操作 Xは、スレッドBにおけるアquireフェンスFと同期します。ただし、以下の条件を満たす場合です。
- アトミック読み取りY(任意のメモリ順序)が存在する。
- YはX(またはXが先行するリリースシーケンス)によって書き込まれた値を読み取る。
- スレッドBにおいて、YはFより順序付けられている。
この場合、スレッドAにおいてXよりも順序付けられているすべての非アトミックおよび緩和されたアトミックストアは、スレッドBにおいてFの後に同じ場所に対して行われるすべての非アトミックおよび緩和されたアトミックロードよりも 先に発生 します。
[編集] フェンス-フェンス同期
スレッドAにおけるリリースフェンスFAは、スレッドBにおけるアquireフェンスFBと同期します。ただし、以下の条件を満たす場合です。
- アトミックオブジェクトMが存在する。
- スレッドAにおいてMを変更するアトミック書き込みX(任意のメモリ順序)が存在する。
- スレッドAにおいて、FAはXより順序付けられている。
- スレッドBにおいて、アトミック読み取りY(任意のメモリ順序)が存在する。
- YはXによって書き込まれた値(または、Xがリリース操作であった場合、Xが先行するリリースシーケンス によって書き込まれるであろう値)を読み取る。
- スレッドBにおいて、YはFBより順序付けられている。
この場合、スレッドAにおいてFAよりも順序付けられているすべての非アトミックおよび緩和されたアトミックストアは、スレッドBにおいてFBの後に同じ場所に対して行われるすべての非アトミックおよび緩和されたアトミックロードよりも 先に発生 します。
[編集] パラメータ
| order | - | このフェンスによって実行されるメモリ順序 |
[編集] 戻り値
(なし)
[編集] 注記
x86(x86-64を含む)では、atomic_thread_fence 関数はCPU命令を発行せず、コンパイル時のコード移動にのみ影響します。ただし、std::atomic_thread_fence(std::memory_order_seq_cst) を除きます。
atomic_thread_fence は、同じ std::memory_order を持つアトミックストア操作よりも強力な同期制約を課します。アトミックストア-リリース操作は、先行するすべての読み取りと書き込みをストア-リリースより先に移動できないようにしますが、std::memory_order_release順序を持つatomic_thread_fenceは、先行するすべての読み取りと書き込みを後続のすべてのストアより先に移動できないようにします。
フェンス-フェンス同期は、たとえば、複数の緩和されたアトミック操作のシーケンスに同期を追加するために使用できます。
// Global std::string computation(int); void print(std::string); std::atomic<int> arr[3] = {-1, -1, -1}; std::string data[1000]; //non-atomic data // Thread A, compute 3 values. void ThreadA(int v0, int v1, int v2) { // assert(0 <= v0, v1, v2 < 1000); data[v0] = computation(v0); data[v1] = computation(v1); data[v2] = computation(v2); std::atomic_thread_fence(std::memory_order_release); std::atomic_store_explicit(&arr[0], v0, std::memory_order_relaxed); std::atomic_store_explicit(&arr[1], v1, std::memory_order_relaxed); std::atomic_store_explicit(&arr[2], v2, std::memory_order_relaxed); } // Thread B, prints between 0 and 3 values already computed. void ThreadB() { int v0 = std::atomic_load_explicit(&arr[0], std::memory_order_relaxed); int v1 = std::atomic_load_explicit(&arr[1], std::memory_order_relaxed); int v2 = std::atomic_load_explicit(&arr[2], std::memory_order_relaxed); std::atomic_thread_fence(std::memory_order_acquire); // v0, v1, v2 might turn out to be -1, some or all of them. // Otherwise it is safe to read the non-atomic data because of the fences: if (v0 != -1) print(data[v0]); if (v1 != -1) print(data[v1]); if (v2 != -1) print(data[v2]); }
[編集] 例
メールボックスの配列をスキャンし、自分宛てのメッセージのみを処理します。不要な同期は行いません。この例ではアトミック-フェンス同期を使用しています。
const int num_mailboxes = 32; std::atomic<int> mailbox_receiver[num_mailboxes]; std::string mailbox_data[num_mailboxes]; // The writer threads update non-atomic shared data // and then update mailbox_receiver[i] as follows: mailbox_data[i] = ...; std::atomic_store_explicit(&mailbox_receiver[i], receiver_id, std::memory_order_release); // Reader thread needs to check all mailbox[i], but only needs to sync with one. for (int i = 0; i < num_mailboxes; ++i) if (std::atomic_load_explicit(&mailbox_receiver[i], std::memory_order_relaxed) == my_id) { // synchronize with just one writer std::atomic_thread_fence(std::memory_order_acquire); // guaranteed to observe everything done in the writer thread // before the atomic_store_explicit() do_work(mailbox_data[i]); }
[編集] 関連
| (C++11) |
与えられたアトミック操作に対するメモリ順序制約を定義する (enum) |
| (C++11) |
スレッドと、同じスレッドで実行されるシグナルハンドラとの間のフェンス (関数) |
| C言語ドキュメント for atomic_thread_fence
| |