単純化しすぎているのかもしれませんが、並べ替えやキャッシュの一貫性などに関する説明は詳細すぎると思います。
では、なぜ MemoryBarrier は実際の読み取りの後に来るのでしょうか? int の代わりに object を使用する例でこれを説明しようとします。
正しいと思う人もいるかもしれません: スレッド 1 はオブジェクトを作成します (その内部データを初期化します)。スレッド 1 は、オブジェクトを変数に入れます。次に、「フェンスを実行」し、すべてのスレッドが新しい値を認識します。
次に、読み取りは次のようになります。スレッド 2 は「フェンスを実行します」。スレッド 2 は、オブジェクト インスタンスを読み取ります。スレッド 2 は、そのインスタンスのすべての内部データを確実に持っています (フェンスで開始されたため)。
これに関する最大の問題は、スレッド 1 がオブジェクトを作成して初期化することです。スレッド 1 は、オブジェクトを変数に入れます。スレッドがキャッシュをフラッシュする前に、CPU 自体がキャッシュの一部をフラッシュします... 変数のアドレスのみをコミットします (その変数の内容ではありません)。
その時点で、スレッド 2 はすでにキャッシュをフラッシュしていました。したがって、メインメモリからすべてを読み取ることになります。したがって、変数を読み取ります(そこにあります)。次に、コンテンツを読み取ります (存在しません)。
最後に、CPU 1 はフェンスを行う Thread 1 を実行します。
では、揮発性の書き込みと読み取りはどうなるでしょうか。揮発性書き込みは、オブジェクトの内容をすぐにメモリに移動させ (フェンスから開始)、変数を設定します (実際のメモリにすぐに移動しない場合があります)。次に、揮発性読み取りは最初にキャッシュをクリアします。次に、フィールドを読み取ります。フィールドの読み取り時に値を受け取った場合、その参照が指す内容が実際にそこにあることは確実です。
これらのささいなことによって、はい、VolatileWrite(1) を実行しても、別のスレッドがゼロの値を表示する可能性があります。しかし、他のスレッドが (揮発性読み取りを使用して) 値 1 を確認するとすぐに、参照される可能性のある必要な他のすべての項目が既に存在します。古い値 (0 または null) を読み取るときに、必要なものがまだすべて揃っていないことを考えると、単純に進行しない可能性があるため、実際にはそれを伝えることはできません。
キャッシュが 2 回フラッシュされたとしても、正しいパターンは次のようになるという議論を既にいくつか見ました。
MemoryBarrier
- この呼び出しの前に変更された他の変数をフラッシュ
します。
読み取りには同じものが必要
です。
MemoryBarrier の後に何かが表示され、既に読み取られている可能性があるため、内容にアクセスするには別の MemoryBarrier を配置する必要があります。
.Net に存在する場合、これらは 2 つの Write-Fence または 2 つの Read-Fence である可能性があります。
私が言ったことすべてについてはわかりません...これは私が得た多くの情報の「編集」であり、VolatileRead と VolatileWrite が逆に見える理由を本当に説明していますが、それらを使用するときに無効な値が読み取られないことも保証します。