私はに関するいくつかの記事、講演、stackoverflowの質問を聞いて読みましstd::atomic
たが、私はそれをよく理解していることを確認したいと思います。MESI(または派生した)キャッシュコヒーレンシプロトコル、ストアバッファー、キューの無効化などで遅延が発生する可能性があるため、キャッシュライン書き込みの可視性とまだ少し混乱しています。
私はx86がより強力なメモリモデルを持っていることを読みました。キャッシュの無効化が遅れると、x86は開始された操作を元に戻すことができます。しかし、私は今、プラットフォームに関係なく、C ++プログラマーとして何を想定すべきかについてのみ興味があります。
[T1:スレッド1 T2:スレッド2 V1:共有アトミック変数]
std :: atomicは、
(1)変数でデータ競合は発生しません(キャッシュラインへの排他的アクセスのおかげ)。
(2)使用するmemory_orderに応じて、(バリアを使用して)順次整合性が発生することが保証されます(バリアの前、バリアの後、またはその両方)。
(3)T1のアトミック書き込み(V1)の後、T2のアトミックRMW(V1)はコヒーレントになります(そのキャッシュラインはT1に書き込まれた値で更新されます)。
しかし、キャッシュコヒーレンシープライマーの言及として、
これらすべての意味は、デフォルトで、ロードが古いデータをフェッチできることです(対応する無効化リクエストが無効化キューに置かれていた場合)。
それで、次は正しいですか?
(4)std::atomic
T2がT1のアトミックwrite(V)の後にアトミックread(V)の「古い」値を読み取らないことを保証しません。
(4)が正しいかどうかの質問:T1のアトミック書き込みが遅延に関係なくキャッシュラインを無効にする場合、アトミックRMW操作がアトミック読み取りではなく無効化が有効になるのをT2が待機しているのはなぜですか?
(4)が間違っている場合の質問:スレッドは実行時に「古くなった」値を読み、「それが見える」のはいつですか?
回答ありがとうございます
アップデート1
だから私はそのとき(3)について間違っていたようです。次のインターリーブを想像してください。最初のV1 = 0の場合:
T1: W(1)
T2: R(0) M(++) W(1)
この場合、T2のRMWはW(1)の後に完全に発生することが保証されていますが、それでも「古い」値を読み取ることができます(私は間違っていました)。これによると、atomicは完全なキャッシュコヒーレンシを保証せず、順次一貫性のみを保証します。
アップデート2
(5)この例を想像してみてください(x = y = 0でアトミックです):
T1: x = 1;
T2: y = 1;
T3: if (x==1 && y==0) print("msg");
私たちが話した内容によると、画面に表示された「msg」を表示しても、T1の後にT2が実行された以上の情報は得られません。したがって、次のいずれかの実行が発生した可能性があります。
- T1 <T3 <T2
- T1 <T2 <T3(T3はx = 1を認識していますが、y = 1はまだ認識していません)
そうですか?
(6)スレッドが常に「古い」値を読み取ることができる場合、通常の「公開」シナリオをとったが、一部のデータの準備ができていることを通知するのではなく、反対のこと(データの削除)を行うとどうなりますか?
T1: delete gameObjectPtr; is_enabled.store(false, std::memory_order_release);
T2: while (is_enabled.load(std::memory_order_acquire)) gameObjectPtr->doSomething();
T2は、is_enabledがfalseであることがわかるまで、削除されたptrを引き続き使用します。
(7)また、スレッドが「古い」値を読み取る可能性があるという事実は、1つのロックフリーアトミック権限だけではミューテックスを実装できないことを意味しますか?スレッド間の同期メカニズムが必要になります。ロック可能なアトミックが必要ですか?