27

std::memory_order_acquireとの私の理解は次のstd::memory_order_releaseとおりです。

取得とは、取得フェンスのに出現するメモリ アクセスをフェンスの前に並べ替えることができないことを意味します。

リリースとは、リリース フェンスのに出現するメモリ アクセスをフェンスの後に並べ替えることができないことを意味します。

私が理解できないのは、特に C++11 アトミック ライブラリでは、取得フェンスがロード操作に関連付けられ、リリース フェンスがストア操作に関連付けられている理由です。

明確にするために、C++11<atomic>ライブラリでは、メモリ フェンスを次の 2 つの方法で指定できます。

x.load(std::memory_order_acquire);

または、次std::memory_order_relaxedのようにフェンスを個別に使用して指定することもできます。

x.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);

私が理解していないのは、上記の acquire と release の定義を考えると、なぜ C++11 がacquireloadに、releasestoreに関連付けるのかということです。はい、取得/ロードとリリース/ストアを使用してスレッド間で同期する方法を示す多くの例を見てきましたが、一般的には、取得フェンス (ステートメントの後のメモリの並べ替えを防ぐ) とリリースのアイデアのようです。フェンス (ステートメントの前にメモリの並べ替えを防止する) は、ロードとストアの考え方と直交しています。

では、たとえば、コンパイラが次のように言わないのはなぜですか。

x.store(10, std::memory_order_acquire);

memory_order_relaxedを使用してから別のステートメントを使用することで上記を達成できるatomic_thread_fence(memory_order_acquire)ことはわかっていますが、もう一度、ストアを直接使用できないのはなぜmemory_order_acquireですか?

これの考えられるユースケースは、他のスレッドに影響を与える可能性のある他のステートメントが実行される前にx = 10、ストア、たとえばが発生するようにしたい場合です。

4

3 に答える 3

31

いくつかのデータを書き込んでから、データの準備ができたことを示す指示を書き込んだとします。データの準備ができたことを示す他のスレッドが、データ自体の書き込みを認識しないようにすることが不可欠です。そのため、以前の書き込みはその書き込みを超えることはできません。

いくつかのデータの準備ができていることを読んだとしましょう。データの準備が整ったことを確認した読み取りの後に実行することが不可欠です。したがって、後続の読み取りはその読み取りの後ろに移動できません。

そのため、同期書き込みを行うときは、通常、それより前に行ったすべての書き込みが、同期書き込みを見る人に見えるようにする必要があります。また、同期読み取りを行う場合、通常、その後に行う読み取りはすべて、同期読み取りの後に行う必要があります。

別の言い方をすれば、取得は通常、リソースを取得またはアクセスできる読み取りであり、後続の読み取りと書き込みはその前に移動してはなりません。リリースは通常、リソースを使い終わったことを書くことであり、以前の書き込みをその後に移動してはなりません。

于 2016-04-29T20:37:51.020 に答える
3

(質問の最初の部分の間違いを修正する部分的な回答 。David Schwartz の回答は、あなたが求めている主な質問を既にうまくカバーしています。取得/解放に関する Jeff Preshing の記事も、別の見方をするのに適しています。)


取得/解放に指定した定義は、フェンスには間違っています。それらは取得操作と解放操作にのみ適用されx.store(mo_release)ますstd::atomic_thread_fence(mo_release)

  • 取得とは、取得フェンスの後に出現するメモリ アクセスをフェンスの前に並べ替えることができないことを意味します。[間違っています。取得操作については正しいでしょう]

  • リリースとは、リリース フェンスの前に出現するメモリ アクセスをフェンスの後に並べ替えることができないことを意味します。[間違っています。リリース操作については正しいでしょう]

それらはフェンスには不十分です。そのため、ISO C++ にはフェンスの取得 (LoadStore / LoadLoad の再順序付けのブロック) およびフェンスの解放 (LoadStore / StoreStore) のより強力な順序付けルールがあります。

もちろん、ISO C++ は「並べ替え」を定義していません。これは、アクセスしているグローバルな一貫した状態があることを意味します。代わりに ISO C++

Jeff Preshing の記事は、ここに関連しています。

  • Acquire and Release Semantics (ロード、ストア、RMW などの取得/解放操作)
  • Acquire and Release Fences Don't Work the Way Youd Expectedでは、オペレーションとは異なり、フェンスに対しては、これらの一方向バリアの定義が正しくなく、不十分である理由を説明しています。(これは、フェンスがプログラムの一方の端まで並べ替えられ、操作自体に関連付けられていないため、すべての操作が相互に順序付けされていないためです。)

これの考えられるユースケースは、他のスレッドに影響を与える可能性のある他のステートメントが実行される前に、x = 10 などのストアが発生することを確認したい場合です。

その「他のステートメント」がアトミック共有変数からのロードである場合、実際にはstd::memory_order_seq_cstStoreLoad の並べ替えを避ける必要があります。 acquire//releaseそれacq_relをブロックしません。

アトミック ストアが他のアトミック ストアの前に表示されることを確認する場合、通常の方法は、2 番目のアトミック ストアで を使用することmo_releaseです。

2 番目のストアがアトミックでない場合、リーダーがデータ競合 UB なしで値を観察できる方法で安全に同期できる可能性はほとんどありません。

(ただし、コンパイラが最適化できるようにするために、ペイロードにプレーンな非オブジェクトを使用する SeqLock をハックするときに、リリースフェンスatomicのユース ケースに遭遇します。しかし、これは実装固有の動作であり、std::アトミックなものは、実際の CPU 用にコンパイルされます。たとえば、32 ビットのアトミックを使用した 64 ビットのアトミック カウンターの実装を参照してください。)

于 2021-12-02T02:10:57.717 に答える