ここで銃を飛ばしているかもしれませんが、ここで2つの問題を混同しているように思えます.
1 つはアトミック性です。これは、単一の操作 (複数の手順が必要になる場合があります) が別の単一の操作と競合してはならないことを意味します。
もう 1 つはボラティリティです。この値はいつ変化すると予想され、その理由は何ですか。
最初を取る。2 段階の操作で現在の値を読み取り、変更し、書き戻す必要がある場合、この操作全体を単一の CPU 命令に変換できない限り、確実にロックが必要になります。データの単一キャッシュライン。
ただし、2 つ目の問題は、ロックを行っている場合でも、他のスレッドからは何が見えるかということです。
.NETのvolatile
フィールドは、任意の時点で変更できることをコンパイラが認識しているフィールドです。シングルスレッドの世界では、変数の変更は命令のシーケンシャル ストリームのある時点で発生するものであるため、コンパイラは変数を変更するコードを追加したとき、または少なくとも外部に呼び出したときを認識します。コードが返されると、呼び出し前と同じ値ではない可能性があるため、変更されている場合とされていない場合があります。
この知識により、コンパイラは、ループまたは同様のコード ブロックの前に、フィールドから値を一度レジスタに持ち上げることができ、その特定のコードのフィールドから値を再読み取りすることはありません。
ただし、マルチスレッドを使用すると、いくつかの問題が発生する可能性があります。1 つのスレッドが値を調整した可能性があり、別のスレッドは、最適化のために、この値が変更されていないことを認識しているため、しばらくの間この値を読み取ることはありません。
volatile
したがって、基本的にコンパイラに、値が必要になるたびにスナップショットを取得する場合を除いて、任意の時点でこれの現在の値があると想定してはならないことを伝えているときに、フィールドにフラグを立てる場合。
ロックは複数ステップの操作を解決し、揮発性はコンパイラーがフィールド値をレジスターにキャッシュする方法を処理し、一緒になってさらに多くの問題を解決します。
また、単一の cpu-instruction で読み取ることができない何かがフィールドに含まれている場合、そのフィールドへの読み取りアクセスもロックする必要がある可能性が高いことに注意してください。
たとえば、32 ビット CPU で 64 ビット値を書き込んでいる場合、その書き込み操作には 2 つのステップが必要であり、別の CPU 上の別のスレッドがステップ 2 の前に 64 ビット値を読み取ることができた場合完了すると、以前の値の半分と新しい値の半分がうまく混ざり合って取得されます。これは、古い値を取得するよりもさらに悪い場合があります。
編集:コメントに答えるにvolatile
は、読み取り/書き込み操作の原子性を保証します。これは、ある意味では真実です。volatile
キーワードは32ビットより大きいフィールドには適用できず、実際にはフィールドをシングルにするからです。 cpu-instruction 32 ビットと 64 ビットの両方の CPU で読み取り/書き込み可能。はい、値が可能な限りレジスタに保持されるのを防ぎます。
そのため、コメントの一部が間違っており、volatile
64 ビット値には適用できません。
volatile
また、読み取り/書き込みの並べ替えに関するセマンティクスがあることに注意してください。
関連情報については、MSDN のドキュメントまたはC# の仕様を参照してください。こちらのセクション 10.5.3.