web-dev-qa-db-ja.com

連動して揮発性

状態を表すために使用している変数があります。複数のスレッドから読み書きできます。

Interlocked.ExchangeInterlocked.CompareExchangeを使用して変更しています。しかし、私はそれを複数のスレッドから読んでいます。

volatileを使用して、変数がローカルにキャッシュされないようにすることができますが、常にメモリから直接読み取ることができます。

ただし、変数をvolatileに設定すると、volatileの使用と、refを使用したInterlockedメソッドへの受け渡しに関する警告が生成されます。

各スレッドが変数の最新の値を読み取っていて、キャッシュされたバージョンではないことを確認したいのですが、volatileを使用できません。

Interlocked.Readがありますが、64ビットタイプ用であり、コンパクトフレームワークでは使用できません。そのドキュメントには、32ビットタイプは1回の操作ですでに実行されているため、必要ないと記載されています。

すべてのアクセスにインターロックメソッドを使用している場合は、揮発性を必要としないというインターネット上の声明があります。ただし、インターロックメソッドを使用して32ビット変数を読み取ることはできないため、すべてのアクセスにインターロックメソッドを使用する方法はありません。

ロックを使用せずに変数のスレッドセーフな読み取りと書き込みを実行する方法はありますか?

66
trampster

_Interlocked.Xxx_関数( この質問 を参照)を使用している場合は、常に揮発性の操作を行うため、この警告は無視してかまいません。したがって、volatile変数は共有状態に対して完全にOKです。どうしても警告を取り除きたい場合は、実際にはcanInterlocked.CompareExchange (ref counter, 0, 0)とのインターロック読み取りを実行できます。

編集:実際には、状態変数にvolatileが必要ですのみ直接書き込む(つまり、_Interlocked.Xxx_を使用しない)。 jerryjvlが述べたように 、インターロック(または揮発性)操作で更新された変数の読み取りでは、最新の値が使用されます。

39
Anton Tykhyy

インターロック操作と揮発性は、実際には同時に使用されることは想定されていません。警告が表示される理由は、(ほとんど?)常に自分がしていることを誤解していることを示しているためです。

過度に単純化して言い換える:
volatileは、変数を更新する他のスレッドが存在する可能性があるため、すべての読み取り操作でメモリから再読み取りする必要があることを示します。実行しているアーキテクチャによってアトミックに読み取り/書き込みが可能なフィールドに適用する場合、long/ulongを使用していない限り、これで十分です。他のほとんどのタイプはアトミックに読み取り/書き込みが可能です。

フィールドが揮発性としてマークされていない場合、Interlocked操作を使用して同様の保証を行うことができます。これにより、キャッシュがフラッシュされ、更新が他のすべてのプロセッサに表示されるようになります...これには利点があります読み取りではなく更新にオーバーヘッドをかけること。

これらの2つのアプローチのどちらが最も効果的かは、正確に何をしているかによって異なります。そして、この説明は非常に単純化されすぎています。しかし、このことから、両方を同時に行うことは無意味であることは明らかです。

40
jerryjvl