取得ロードは他の取得操作で並べ替えることができますか?cppreferenceは、非アトミックでリラックスしたものだけが取得によって順序付けられると言います

Aug 22 2020

C ++リファレンスによると、mutex.lock()memory_order_acquire操作でありmutex.unlock()memory_order_release操作です。

ただし、memory_order_acquireおよびmemory_order_releaseは非アトミックおよびリラックスアトミック操作にのみ有効です。

memory_order:リリース-cppreferenceで順序を取得します

スレッドAのアトミックストアにタグが付けられmemory_order_release、同じ変数からのスレッドBのアトミックロードにタグが付けられている場合、スレッドAmemory_order_acquireの観点からアトミックストアの前に発生したすべてのメモリ書き込み(非アトミックおよびリラックスアトミック)。スレッドBで目に見える副作用になる

C ++のミューテックスはアトミック操作の可視性を保証できますか?例は次のとおりです。のA前にコードを並べ替えることができ、mu.lock()スレッドbは次のxように読み取られfalseますか?


#include <thread>
#include <atomic>
#include <cassert>
#include <iostream>
#include <unistd.h>

std::atomic<bool> x = {false};
std::mutex mu;

void write_x(){
  mu.lock();
  std::cout << "write_x" << std::endl;
  x.store(true, std::memory_order_release);
  mu.unlock();
}

void read_x() {
  mu.lock();
  std::cout << "read_x" << std::endl;
  assert(x.load(std::memory_order_acquire)); // A
  mu.unlock();
}

int main() {
  std::thread a(write_x);
  usleep(1);
  std::thread b(read_x);

  a.join(); b.join();

  return 0;
}

回答

4 PeterCordes Aug 23 2020 at 06:25

TL:DR:「すべてのメモリ書き込み」とは、言及されている種類だけでなく、すべてを意味しますが、言い回しは混乱を招きます。おそらく非アトミックでリラックスしたアトミックopsでさえ、同期全体で安全に表示されることを指摘することを目的としていますが、フレージングには「含む」という単語がありません。


cppreferenceは、標準を説明することを目的としたwikiであることに注意してください。これは規範的な技術用語ではなく、ISO C ++標準とは異なる用語で説明することもあります。

それは一般的に非常に良いですが、何かが奇妙に見えるときにそれが完璧であると単に仮定しないでください。周囲の文脈(および正気)から、資格のない「すべて」と言っている段落の最後の文のように、それが意味されていることはまだかなり明白です。


ISO C ++は明確です。リリース操作を「見る」取得操作は、同期との関係を作成します。リリース前のすべては、取得操作後のコードに表示されます。

したがって、グローバルにコヒーレントな共有メモリ状態にアクセスする操作が、取得操作によってすべてが前に並べ替えられるのをブロックするモデルの観点から。releaseおよびseq_cst操作を含みます。(注cppreferenceのこの部分はへの参照がありませんことを再のちょうど保証視界にかどうか、順序を。グローバルコヒーレント状態へのアクセスのローカル並べ替えは、それが道というものを記述するために、多くの場合、より便利ですので、どのように本当のCPUの仕事、実際にあります、あなたが質問でやっているように。)

これは、C ++の取得と解放の定義が、非常識な魔法の例外なしに標準の用語と一致することを意味します。 https://preshing.com/20120913/acquire-and-release-semantics/


一部の人々は「リラックスしたアトミック」を使用して、より弱いすべての順序を説明していることに注意してくださいseq_cst。例:ハーブサッターは、この質問についての話でそれをそのように使用します。

それはそのcppreference定義で意味されたものかもしれませんが、IDKはなぜ彼らが除外したいのかseq_cst。すべてのアトミック操作と非アトミック操作が順序付けられます。だからおそらく彼らは意味していましたmo_relaxed、そしてそれらでさえ注文されている/目に見えることを指摘したかっただけです。

seq_cst既にご注文に言うことができ、それ自体がすべて。WRT他に、その「もちろん」とは、取得および解放操作に関して命じた。しかし、その理由は考えにくいです。)


弱い順序もそれによって命令されたという事実を強調することを意図した場合、それらは「非アトミックおよびリラックスアトミックを含む」と書くべきでした。「含む」という言葉がなければ、その言い回しは、非アトミックでリラックスしたアトミックのみを意味すると読むことができます。全体像と何が正気であるかどうかを理解することによってのみ、正しい読み方を得ることができます。

正確に理解する必要のあるテクニカルライティングでは、「含むがこれに限定されない」というフレーズがよく使用されます。


また、心配した理由だけでなく、例でもアサートをトリガーできることに注意してください

スレッドのa起動が遅い場合、スレッドはb最初にクリティカルセクションに入りx、他のスレッドでのprint + storeが発生する前にprint + readを実行する可能性があります。

そのようなおもちゃの例を書く通常の方法は、値が表示されるまで取得ロードでスピンするループです。たとえば、data_read気になるストアの後にリリース操作によって保存されるようなフラグです。これにより、読み取り側が、書き込み側の解放操作と同期した取得操作の後に実行されることがわかります。