最悪(実際には機能しない)
のアクセス修飾子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が順序を変更する問題を回避できるのか、あるいはvolatileとインクリメントを組み合わせる必要があるのかは、完全にはわかりません。
インターロックされたメモ:
- インターロックされたメソッドは、任意の数のコアまたはCPUで同時に安全です。
- インターロックされたメソッドは、実行する命令の周囲に完全なフェンスを適用するため、並べ替えは発生しません。
- インターロックされたメソッドは、揮発性フィールドへのアクセスを必要とせず、サポートもしません。
脚注:揮発性が実際に何に適しているか。
などvolatile
のためにそれを何、マルチスレッドの問題のこれらの種類を防ぐことはできませんか?良い例は、2つのスレッドがあることです。1つは常に変数に書き込み(たとえばqueueLength
)、もう1つは常に同じ変数から読み取ります。
queueLength
が揮発性でない場合、スレッドAは5回書き込みを行う可能性がありますが、スレッドBはそれらの書き込みが遅延している(または誤った順序である可能性さえある)と見なす場合があります。
解決策はロックすることですが、この状況ではvolatileを使用することもできます。これにより、スレッドAが書き込んだ最新のものがスレッドBに常に表示されるようになります。ただし、このロジックが機能するのは、読み取りを行わないライターと書き込みを行わないリーダーがあり、作成しているものがアトミック値である場合のみです。単一の読み取り-変更-書き込みを行うとすぐに、インターロックされた操作に移動するか、ロックを使用する必要があります。