最悪(実際には機能しません)
のアクセス修飾子をに変更しcounter
ますpublic volatile
他の人が言及しているように、これ自体は実際にはまったく安全ではありません。ポイントvolatile
は、複数の CPU で実行されている複数のスレッドがデータをキャッシュし、命令を並べ替えることができるということです。
そうでない volatile
場合、CPU A が値をインクリメントすると、CPU B はそのインクリメントされた値を実際には時間が経つまで認識しない可能性があり、問題が発生する可能性があります。
である場合volatile
、これにより、2 つの CPU が同じデータを同時に参照できるようになります。回避しようとしている問題である読み取り操作と書き込み操作のインターリーブをまったく停止しません。
セカンドベスト:
lock(this.locker) this.counter++
;
lock
これは安全に実行できます (アクセスする他のすべての場所を覚えている場合this.counter
)。によって保護されている他のコードを他のスレッドが実行するのを防ぎますlocker
。また、ロックを使用すると、上記のようなマルチ CPU の並べ替えの問題を防ぐことができます。これはすばらしいことです。
問題は、ロックが遅いことです。実際には関係のない他の場所で を再利用するとlocker
、理由もなく他のスレッドをブロックしてしまう可能性があります。
一番
Interlocked.Increment(ref this.counter);
これは、中断できない「1 回のヒット」で読み取り、インクリメント、および書き込みを効果的に行うため、安全です。このため、他のコードに影響を与えることはなく、他の場所でロックすることを覚えておく必要もありません。また、非常に高速です (MSDN によると、最新の CPU では、これは文字通り単一の CPU 命令であることがよくあります)。
ただし、他のCPUの並べ替えを回避するかどうか、または揮発性とインクリメントを組み合わせる必要があるかどうかは完全にはわかりません。
インターロック注記:
- インターロックされた方法は、任意の数のコアまたは CPU で同時に安全です。
- インターロックされたメソッドは、実行する命令の周囲に完全なフェンスを適用するため、並べ替えは発生しません。
- インターロックされたメソッドは、 volatile フィールドへのアクセスを必要としないか、サポートしません。 volatile は、特定のフィールドの操作の周りにハーフ フェンスを配置し、インターロックはフル フェンスを使用するためです。
脚注: volatile が実際に役立つもの。
このvolatile
種のマルチスレッドの問題を防げないのに、何のためにあるのでしょうか? 良い例は、2 つのスレッドがあるとします。1 つは常に変数 (たとえばqueueLength
) に書き込み、もう 1 つは常に同じ変数から読み取ります。
が揮発性でない場合queueLength
、スレッド A は 5 回書き込みを行う可能性がありますが、スレッド B はそれらの書き込みが遅延していると見なす可能性があります (または、場合によっては間違った順序である可能性さえあります)。
解決策はロックすることですが、この状況では volatile を使用することもできます。これにより、スレッド B は、スレッド A が書いた最新のものを常に見ることができます。ただし、このロジックは、読み取りをまったく行わないライターと書き込みを行わないリーダーがいて、書き込みが原子値である場合にのみ機能することに注意してください。単一の読み取り-変更-書き込みを行うとすぐに、インターロック操作に移動するか、ロックを使用する必要があります。