1. ホーム
  2. c#

[解決済み] 揮発性 vs. 連動性 vs. ロック性

2022-03-21 21:49:50

質問

あるクラスが public int counter フィールドは、複数のスレッドからアクセスされます。この int はインクリメントまたはデクリメントされるだけです。

このフィールドをインクリメントするには、どの方法を使うべきか、またその理由は?

  • lock(this.locker) this.counter++; ,
  • Interlocked.Increment(ref this.counter); ,
  • のアクセスモディファイアを変更します。 counterpublic volatile .

今、私が発見したのは volatile を削除しています。 lock ステートメントを使用し Interlocked . しかし、これをしない理由はあるのでしょうか?

どのように解決するのですか?

最悪(実際には動作しない)

<ブロッククオート

のアクセス修飾子を変更する。 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);

これは、読み込み、インクリメント、書き込みを「一発」で行うので安全であり、中断されることはありません。このため、他のコードに影響を与えることはありませんし、他の場所をロックすることを覚える必要もありません。また、非常に高速です(MSDNによると、最近のCPUでは、これは文字通り1つのCPU命令であることが多いようです)。

しかし、他のCPUの再順序付けを回避できるのか、揮発性とインクリメントを組み合わせる必要があるのか、まったくわかりません。

インターロックノートです。

  1. インターロックされたメソッドは、コアやCPUの数に関係なく連続的に安全です。
  2. インターロックされたメソッドは、実行する命令の周りに完全なフェンスを適用するため、順序の入れ替えは起こりません。
  3. インターロック・メソッド volatileフィールドへのアクセスを必要としない、あるいはサポートしない。 volatileは与えられたフィールドに対する操作の周りに半分のフェンスを置き、interlockedは完全なフェンスを使用するからです。

脚注:volatileが実際に適していること。

として volatile は、このようなマルチスレッドの問題を防ぐことはできませんが、何のためにあるのでしょうか?例えば、2つのスレッドがあり、1つは常に変数に書き込みをする (例えば queueLength と、その同じ変数から常に読み取るものです。

もし queueLength が揮発性でない場合、スレッドAは5回書き込むことができますが、スレッドBはこれらの書き込みが遅れている(あるいは間違った順序である可能性もある)と見なすかもしれません。

解決策としてはロックすることですが、この状況ではvolatileを使用することもできます。これにより、スレッドBは常にスレッドAが書き込んだ最新のものを見ることができるようになります。しかし、このロジックは のみ は、決して読まない書き手と、決して書かない読み手がいる場合に有効です。 そして 書き込むものがアトム値である場合。読み取り・変更・書き込みを1回でも行うと、すぐにInterlockedオペレーションに移行するか、Lockを使用する必要があります。