9

私はvolatile変数、理論的に を使用するアプリケーションをマルチスレッド化のための無料のコードをロック実装するには揮発性変数。スレッドAが変数値を更新し、スレッドBがその変数を読み込んだ直後にその変数が読み取られた場合、最近スレッドAから書き込まれた最新の値が表示されます。C#4.0 Nuchhell book in これは、スワップされることから、読み取りに続いて書き込みを防ぐことはできません揮発適用間違ったC#で揮発性とThread.MemoryBarrier

ので

です。

は、この問題は次のよう volatile変数のすべての取得前 Thread.MemoryBarrier()を置くことによって解決さでした:

private volatile bool _foo = false; 

private void A() 
{ 
    //… 
    Thread.MemoryBarrier(); 
    if (_foo) 
    { 
     //do somthing 
    } 
} 

private void B() 
{ 
    //… 
    _foo = true; 
    //… 
} 

そして、これは問題が解決するかどうか。条件の1つでその値に依存するwhileループがあると考えてください。 whileループが問題を解決する正しい方法である前にThread.MemoryBarrier()を入れていますか?例:

private void A() 
{ 
    Thread.MemoryBarrier(); 
    while (_someOtherConditions && _foo) 
    { 
     // do somthing. 
    } 
} 

私は_foo変数がいずれかのスレッドが任意の時点でそれを求めてその最も新鮮な価値を提供したい、より正確です。変数を呼び出す前Thread.MemoryBarrier()を挿入すると、私は_fooの代わりにFooプロパティを使用し、同じようにその財産の取得中にThread.MemoryBarrier()を行うことができ、問題を修正した場合ので:あなたの第二の例では

​​
+0

可能な重複... http://stackoverflow.com/questions/1330590/when-to-use-volatile-or-thread-memorybarrier-in-threadsafe-locking-code-c –

+0

@Aron;それは重複していません。これは別の質問です。 –

+0

@Jalalこのアプローチを使って問題を解決しようとしていますかvolatile/memorybarrierですか、あなたの問題を解決する方法はありますか? @Aron; –

答えて

8

「C#In a Nutshell」は正しいですが、その文は疑問です。どうして?

  • 「揮発性」、それは前に、単一のスレッド
  • 「書き込み」内のロジックに影響を与える場合は、とにかく、プログラムの順序でを発生することが保証されてwithot、「読み取り」に続くA「書き込み」あなたの例では、マルチスレッドプログラムで 'read'は、全く無意味です。です。

明確にしましょう。あなたの元のコードを取る:

スレッドスケジューラは、すでに_foo変数をチェックしたが、それはちょうど//do somethingコメントの前に中断してしまった場合はどうなり
private void A() 
{ 
    //… 
    if (_foo) 
    { 
     //do something 
    } 
} 

?さて、あなたの他のスレッドは_fooの値を変更する可能性があります。つまり、すべての揮発性物質とThread.MemoryBarriersは何もないとカウントされます。 _fooの値がfalseの場合、do_somethingを避けることが絶対に不可欠な場合は、ロックを使用する以外に選択肢はありません。

しかし、_fooが偽になったときにdo somethingが実行されても問題ない場合は、volatileキーワードが必要以上のものであったことを意味します。

明確にする:メモリバリアを使用するように指示しているすべてのレスポンダーが間違っているか、過剰な攻撃を行っています。

+0

あなたの応答に感謝します。 私の場合、可能な限り_fooが真であればdo_somethingが実行されるのを避けたいですが、それは**致命的ではありません**。 MemoryBarrierでもっと正確にすることができれば**揮発性で使うのはなぜでしょうか? _fooチェックの後でスレッドスケジュールが "witch is possible"であれば、do_somthingは実行されますが、少なくとも私たちはチャンス**を減らす**その時点で_fooは間違っているでしょうか? –

+2

@ Jalal: 'volatile'宣言は、変数自体がCPUレジスタに格納されないことをすべて意味しています。メモリの読み込みと書き込みを直接指示します(マルチプロセッサマシン上の他のプロセッサのキャッシュを無効にします)。したがって、明示的なメモリバリアは不要です。 「揮発性」はすでに必要なものを達成しています。 –

+0

@BrentAriasプロセッサがあるプロセッサ上の値を更新するのではなく、一方のスレッドが更新された値を読み込み、もう一方のスレッドが失効した値を読み取る可能性があるため、MemoryBarrier *が必要です。 – Anthony

0

、あなたも置く必要があるだろうループ内にThread.MemoryBarrier();がある場合は、ループ状態を確認するたびに最新の値を取得してください。 hereから引き出さ

+0

これが問題を解決した場合、whileループの閉じ括弧の前とwhileループの呼び出しの前に、Thread.MemoryBarrier()を最後の行に置く必要があります。 –

+0

@Jalal:明示的な 'MemoryBarrier 'これらの例では、' _foo'が 'volatile'とマークされている限り、'呼び出しは必要ありません。 (私は専門家ではありません;おそらく単に 'ロック'を使用します) – LukeH

+2

この場合:前、後、または両方のいずれのThread.MemoryBarrierを使用しても、 。 –

0

...

class Foo 
{ 
    int _answer; 
    bool _complete; 

    void A() 
    { 
    _answer = 123; 
    Thread.MemoryBarrier(); // Barrier 1 
    _complete = true; 
    Thread.MemoryBarrier(); // Barrier 2 
    } 

    void B() 
    { 
    Thread.MemoryBarrier(); // Barrier 3 
    if (_complete) 
    { 
     Thread.MemoryBarrier();  // Barrier 4 
     Console.WriteLine (_answer); 
    } 
    } 
} 

障壁1と4は「0」を書き込むことから、この例 を防ぎます。障壁2と3 は新鮮さの保証を提供します。彼らが012の後に走った場合、 は、 _completeが真と評価されることを保証します。私たちはあなたのループの例に戻ってその場合は

が...これはそれがどのように見えるべきかです...メモリバリアの

private void A() 
{ 
    Thread.MemoryBarrier(); 
    while (_someOtherConditions && _foo) 
    { 
     //do somthing 
     Thread.MemoryBarrier(); 
    } 
} 
+1

@ Aaronありがとう、私はそれが同じ本 "C#4.0 in a Nutshell"にある前にそのリンクをチェックする。しかし、これで問題が解決されたら、whileループの閉じ括弧の前に最後の行にThread.MemoryBarrier() 。 –

+0

@Jalalそれはあなたの「何か」が何をするかによって異なります。スレッドバリヤーがあなたの_someOtherconditions/_fooヴァールに影響するところです –

+1

@Aaron: '_foo'が' volatile'とマークされていれば、明示的な 'MemoryBarrier'これらの特定の例では呼び出しは不要です。 (確かに100%確かではないとはっきりしていて、疑問があれば 'lock'を使用するだけです) – LukeH

0

Microsoft自身の言葉:

メモリバリアのみに必要です(例えば、複数のインテルItaniumプロセッサを使用するシステム)弱いメモリ順序を有するマルチプロセッサシステムである。

ほとんどの場合、C#のlockステートメント、Visual BasicのSyncLockステートメント、またはMonitorクラスを使用すると、データを簡単に同期できます。

+0

http://www.albahari.com/threading/part4.aspxを参照して、マイクロソフト自身の言葉が正しくないことを確認してください!! –

+0

ポスターは、特にロックフリーの実装を求めています。 –

5

本はです。です。
CLRのメモリモデルは、ロード操作とストア操作の順序が変更される可能性があることを示します。これはvolatile変数の不揮発変数になります。

volatileのみロード操作が取得セマンティクスを有するであろう、とストア・オペレーションがセマンティクスを解放有するであろうことを意味する変数の宣言。また、コンパイラは、変数がシリアライズされた単一スレッドの方法でアクセスされるという事実を中継する特定の最適化を実行することを避ける(例えば、ループから巻き上げ/ストアする)。

キーワードだけを使用するとクリティカルセクションが作成されず、スレッド間で魔法のように同期することはありません。

ロックフリーコードを書くときは、慎重ににする必要があります。それについては何も簡単ではなく、専門家でさえそれを正しくするのは難しいです。
あなたが解決しようとしている元の問題が何であれ、それを行うにはもっと合理的な方法があるようです。

+0

@Liran:私は揮発性だけを使用してクリティカルセクションを作成したり、スレッドを互いに同期させたりすることはできないことを知っていました。私はいつでも_foo変数を更新する必要がありますロックフリーコード内。 –

+0

@ Jalal変数をvolatileとして宣言すると、すべてのロード操作が変数の最新の値(たとえば、レジ​​スタの代わりにメインメモリから)を読み込むことが保証されます。コード全体にメモリ障壁を広げるべきではありません。そうすることは、パフォーマンスに大きな悪影響を及ぼす可能性があります。繰り返しますが、ロックフリーのコードを試して試してみるだけでなく、実際の生産コードが絵に表示されている場合は、別の解決策を見つけることをお勧めします。 @Liran; – Liran

+0

;変数をvolatileとして宣言すると、すべての読み込み操作が変数の最新の値を読み取ることが保証されます。私は、http://www.albahari.com/threading/part4.aspxという本から理解しているように、読んだ後でおそらく最も更新された値を得ることはできないでしょう。そう、私はロックフリーコードを実験しようとしています。問題は、揮発性変数を取得する前に 'Thread.MemoryBarrier'を実行すると、アプリケーションのパフォーマンスに大きな悪影響を及ぼすかどうかです。質問の更新 –

関連する問題