Namespaces
Variants

std:: atomic_thread_fence

From cppreference.net
Concurrency support library
Threads
(C++11)
(C++20)
this_thread namespace
(C++11)
(C++11)
Cooperative cancellation
Mutual exclusion
Generic lock management
Condition variables
(C++11)
Semaphores
Latches and Barriers
(C++20)
(C++20)
Futures
(C++11)
(C++11)
(C++11)
Safe reclamation
Hazard pointers
Atomic types
(C++11)
(C++20)
Initialization of atomic types
(C++11) (deprecated in C++20)
(C++11) (deprecated in C++20)
Memory ordering
(C++11) (deprecated in C++26)
atomic_thread_fence
(C++11)
Free functions for atomic operations
Free functions for atomic flags
ヘッダーで定義 <atomic>
extern "C" void atomic_thread_fence ( std:: memory_order order ) noexcept ;
(C++11以降)

memory synchronization ordering を確立します。これは非アトミックアクセスおよび緩和されたアトミックアクセスの同期順序を、 order で指定された通りに、関連するアトミック操作なしで行います。ただし、以下の説明にあるように、同期を設定するには少なくとも一つのアトミック操作が必要であることに注意してください。

目次

Fence-atomic 同期

スレッド A におけるリリースフェンス F は、以下の条件が満たされる場合、スレッド B におけるアトミックな acquire operation Y と同期します (synchronizes-with)。

  • 任意のメモリ順序を持つアトミックストア X が存在し、
  • Y X によって書き込まれた値(または X がリリース操作であった場合、 X を先頭とするリリースシーケンス によって書き込まれる値)を読み取り、
  • スレッド A 内で F X に対してシーケンス前に位置する。

この場合、スレッド A において F に対して sequenced-before 関係にある全ての非アトミック操作および緩和アトミックストアは、 Y 以降にスレッド B で同じメモリ位置から行われる全ての非アトミック操作および緩和アトミックロードに対して happen-before 関係が成立します。

Atomicフェンス同期

アトミックな release操作 X がスレッド A 内で実行され、acquireフェンス F がスレッド B 内に存在する場合、以下の条件が満たされるときに同期関係が成立します:

  • 任意のメモリ順序を持つアトミック読み込み Y が存在し、
  • Y X によって書き込まれた値(または Xが先頭となるリリースシーケンス によって書き込まれた値)を読み取り、
  • Y がスレッド B 内で F に対してシーケンス前に位置する。

この場合、スレッド A において X に対して sequenced-before 関係にある全ての非アトミック操作および緩和アトミックストアは、 F 以降にスレッド B で同じメモリ位置から行われる全ての非アトミック操作および緩和アトミックロードに対して happen-before 関係が成立します。

Fence-fence 同期

スレッド A のリリースフェンス FA は、スレッド B のアクワイアフェンス FB と同期する(synchronizes-with)、もし

  • アトミックオブジェクト M が存在し、
  • スレッド A において、任意のメモリ順序で M を変更するアトミック書き込み X が存在し、
  • スレッド A において FA X に対して sequenced-before の関係にあり、
  • スレッド B において、任意のメモリ順序でのアトミック読み込み Y が存在し、
  • Y X によって書き込まれた値(または X がリリース操作であった場合の X を先頭とするリリースシーケンス によって書き込まれる値)を読み取り、
  • スレッド B において Y FB に対して sequenced-before の関係にある。

この場合、スレッド A において FA に対して sequenced-before 関係にあるすべての非アトミック操作および緩和アトミックストアは、 happen-before 関係となり、スレッド B FB 以降に同じメモリ位置から行うすべての非アトミック操作および緩和アトミックロードに対して先行します。

order パラメータの値に応じて、この呼び出しの効果は以下の通りです:

パラメータ

order - このフェンスによって実行されるメモリ順序

注記

x86(x86-64を含む)では、 atomic_thread_fence 関数はCPU命令を発行せず、コンパイル時のコード移動にのみ影響します。ただし、 std :: atomic_thread_fence ( std:: memory_order_seq_cst ) は除きます。

atomic_thread_fence は同じ std::memory_order を持つアトミックストア操作よりも強い同期制約を課します。アトミックストア・リリース操作は先行する全ての読み書きがストア・リリースを超えて移動するのを防ぎますが、 atomic_thread_fence std:: memory_order_release 順序付けを指定した場合、先行する全ての読み書きが後続の全てのストアを超えて移動するのを防ぎます。

Fence-fence同期は、一連の複数の緩和されたアトミック操作に同期を追加するために使用できます。例えば:

// グローバル
std::string computation(int);
void print(std::string);
std::atomic<int> arr[3] = {-1, -1, -1};
std::string data[1000]; //非アトミックデータ
// スレッドA、3つの値を計算
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);
}
// スレッドB、既に計算された0〜3個の値を出力
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は-1になる可能性、一部または全てが設定される可能性がある
//  それ以外の場合、フェンスにより非アトミックデータを安全に読み取ることができる:
    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];
// 書き込みスレッドは非アトミックな共有データを更新し、
// その後以下のようにmailbox_receiver[i]を更新する:
mailbox_data[i] = ...;
std::atomic_store_explicit(&mailbox_receiver[i], receiver_id, std::memory_order_release);
// 読み込みスレッドは全てのmailbox[i]をチェックする必要があるが、同期は1つだけで十分
for (int i = 0; i < num_mailboxes; ++i)
    if (std::atomic_load_explicit(&mailbox_receiver[i],
        std::memory_order_relaxed) == my_id)
    {
        // 1つの書き込みスレッドとのみ同期
        std::atomic_thread_fence(std::memory_order_acquire);
        // atomic_store_explicit()の前に書き込みスレッドで行われた全ての操作を
        // 確実に観測できることが保証される
        do_work(mailbox_data[i]);
    }

関連項目

指定されたアトミック操作に対するメモリ順序制約を定義する
(列挙型)
同じスレッド内で実行されるスレッドとシグナルハンドラ間のフェンス
(関数)
Cドキュメント for atomic_thread_fence