例:
Thread a: Interlocked.Increment(ref x);
Thread b: int currentValue = x;
スレッド a の後にスレッド b が実行されると仮定すると、スレッド b の「currentValue」はインクリメントされた値であることが保証されますか? または、スレッド b は Thread.VolatileRead(ref x) を実行する必要がありますか?
例:
Thread a: Interlocked.Increment(ref x);
Thread b: int currentValue = x;
スレッド a の後にスレッド b が実行されると仮定すると、スレッド b の「currentValue」はインクリメントされた値であることが保証されますか? または、スレッド b は Thread.VolatileRead(ref x) を実行する必要がありますか?
技術的には、.NET が実行されている CPU によって異なりますが、一般的な CPU の場合、答えはイエスです。Interlocked.Increment に対してキャッシュの一貫性が保証されます。これは、MESI の要求に応じてメモリ バリアとして機能します。
CPU はキャッシュ内に無効な行を持つことができますが、その行が無効であることをまだ認識していません。無効化キューには、まだ処理されていない無効化が含まれています。(無効化キューはキャッシュの反対側にあります。CPU はストア バッファと同様にスキャンできません)。その結果、メモリバリアが必要になります。
http://en.wikipedia.org/wiki/MESI_protocol (x86)
MOESI (AMD64 チップで使用) は非常に似ています。
私が理解しているように、Interlocked は、他の Interlocked メソッドによってアクセスされた場合にのみ保証されます。これは、アトミックであることを保証できない x86 システムで 64 ビット値について話す場合に特に重要になる可能性があるため、破損した値が懸念されます。Interlocked によって変更できる値を確実に読み取るための優れたトリックは、CompareExchange です。
int val = Interlocked.CompareExchange(ref field, 0, 0);
これは の値field
を 0 に変更しますが、古い値が 0 の場合のみ - それ以外の場合は何もしません。いずれにせよ、古い値が返されます。つまり、基本的には、値を変更せずに読み取り、他のインターロック メソッドに対して安全です。