30

VolatileRead/write (try...) の例は見つかりません、それでも:

volatilevs はいつ使用する必要がありますVolatileReadか?

AFAIKの全体的な目的は、ハーフフェンスvolatileを作成することです:

  • READ操作の場合、現在の操作の後に来る (他のスレッドでの) 読み取り/書き込みは、フェンスの前には渡されません。したがって、最新の値を読み取ります。

質問1

では、なぜ が必要なのvolatileReadですか? volatileすでに仕事 をしているようです。

さらに、C#では (Java とは異なり)すべての書き込みは揮発性volatileであり、書き込みフィールドが非揮発性フィールドか非揮発性フィールドかに関係なく、なぜ ? が必要なの volatileWriteですか?

質問2

これは の実装ですVolatileRead:

[MethodImpl(MethodImplOptions.NoInlining)]
public static int VolatileRead(ref int address)
{
    int num = address;
    MemoryBarrier();
    return num;
}

int num = address;があるのはなぜですか?それらは、明らかに値を保持しているアドレス引数をすでに持っています。

4

4 に答える 4

37

Thread.VolatileRead / Write()は絶対に使用しないでください。これは.NET1.1の設計ミスであり、完全なメモリバリアを使用しています。これは.NET2.0で修正されましたが、これらのメソッドを修正できなくなり、System.Threading.Volatileクラスによって提供される新しい方法を追加する必要がありました。これはジッターが認識しているクラスであり、jit時にメソッドを特定のプロセッサタイプに適したバージョンに置き換えます。

リファレンスソースから入手できるVolatileクラスのソースコード内のコメントは、物語を物語っています(適合するように編集されています)。

// Methods for accessing memory with volatile semantics.  These are preferred over 
// Thread.VolatileRead and Thread.VolatileWrite, as these are implemented more
// efficiently.
//
// (We cannot change the implementations of Thread.VolatileRead/VolatileWrite 
// without breaking code that relies on their overly-strong ordering guarantees.)
//
// The actual implementations of these methods are typically supplied by the VM at 
// JIT-time, because C# does not allow us to express a volatile read/write from/to 
// a byref arg. See getILIntrinsicImplementationForVolatile() in jitinterface.cpp.

そして、はい、あなたはその使用法の例を見つけるのに苦労するでしょう。リファレンスソースは、スレッド化を処理する、注意深く記述され、テストされ、戦いで傷ついたC#コードのメガバイトを含む優れたガイドです。VolatileRead / Writeを使用する回数:ゼロ

率直に言って、.NETメモリモデルは、CLR mmとC#mmによって行われた矛盾する仮定と、最近ARMコアに追加された新しいルールを伴う混乱です。アーキテクチャごとに異なることを意味するvolatileキーワードの奇妙なセマンティクスは、その証拠です。弱いメモリモデルを備えたプロセッサの場合、通常、C#言語仕様の内容は正確であると想定できます。

ジョー・ダフィーはすべての希望をあきらめ、それをすべて使用することを思いとどまらせていることに注意してください。一般に、言語とフレームワークによって提供されるプリミティブよりも優れた結果が得られると想定するのは非常に賢明ではありません。VolatileクラスのRemarksセクションは、ポイントを持ち帰ります。

通常の状況では、C#lockステートメント、Visual Basic SyncLockステートメント、およびMonitorクラスは、データへのアクセスを同期する最も簡単でエラーが発生しにくい方法を提供し、Lazyクラスは、直接使用せずに遅延初期化コードを記述する簡単な方法を提供します。ロックを再確認しました。

于 2013-02-24T14:27:45.520 に答える
8

フェンスがコードに適用される方法をよりきめ細かく制御する必要がある場合は、static Thread.VolatileReadまたはThread.VolatileWriteを使用できます。

変数volatileを宣言すると、コンパイラはその値をキャッシュせず、常にフィールド値を読み取り、書き込みが実行されると、コンパイラは割り当てられた値をすぐに書き込みます。

Thread.VolatileReadThread.VolatileWriteの2つのメソッドを使用すると、変数を揮発性として宣言せずに、よりきめ細かい制御を行うことができます。キャッシュされて、変数を揮発性と宣言したときにすぐに書き込むので、悪い言葉で言えば、より多くの制御とより多くの自由があります...

VolatileRead()最新バージョンのメモリアドレスを読み取り、VolatileWrite()そのアドレスに書き込み、すべてのスレッドでアドレスを使用できるようにします。変数に両方VolatileRead()VolatileWrite()一貫して使用すると、変数を揮発性としてマークするのと同じ効果があります。

違いを例で説明しているこのブログ投稿を見てください...

行intnum=アドレス; ある ?それらには、値を明確に保持しているアドレス引数がすでにあります。

メソッド内で外部の何かが値を変更しないようにするための防御コピーです。外部からの偶発的な変更を回避するために、整数値がローカル変数にコピーされます。

ノート

Visual Basicにはvolatileキーワードが存在しないため、一貫して静的なメソッドを使用して、c#でvolatileキーワードと同じ効果を実現する唯一の選択肢VolatileRead()ありVolatileWrite()ます。

于 2013-02-23T10:25:38.820 に答える
4

なぜ行 int num = address; ある ?それらは、明らかに値を保持しているアドレス引数をすでに持っています。

addressではありませんint。それはですint*(つまり、実際にはアドレスです)。コードはポインターを逆参照し、それをローカルにコピーして、逆参照の後にバリアが発生するようにします。

于 2013-02-23T10:41:14.523 に答える
1

alerootの答えについて詳しく説明します。

Volatile.Read と Volatile.Write は Royi Namir の議論の volatile 修飾子と同じです。しかし、あなたはそれらを賢く使うことができます。

たとえば、volatile 修飾子を使用してフィールドを宣言すると、このフィールドへのすべてのアクセスは、読み取りまたは書き込み操作であるかどうかに関係なく、解放されていない CPU レジスタから読み取られます。これはほとんどの場合必要ではなく、次の場合に不要なパフォーマンス ヒットになります。フィールドには多くの読み取り操作があります。

プライベート シングルトン変数が volatile として宣言され、プロパティ ゲッターに返されるシナリオを考えてみましょう。一度初期化すると、CPU レジスタからルートを読み取る必要がないため、インスタンスが作成されるまで Volatile.Read / Write を使用できます。作成されると、すべての読み取り操作は通常のフィールドとして実行できます。そうしないと、パフォーマンスが大幅に低下します。

一方、Volatile.Read または Volatile.Write はオンデマンド ベースで使用できます。最適な使用法は、volatile 修飾子なしでフィールドを宣言し、必要に応じて Volatile.Read または Volatile.Write を使用することです。

于 2015-07-09T22:44:54.820 に答える