2012-01-04 11 views
19

は、私はいくつかのループの実行を制御するフィールドを持っていると仮定今これはThread.MemoryBarrier()の正しい使用ですか?</p> <pre><code>private static bool shouldRun = true; </code></pre> <p>そして、私のようなコードがあるスレッドの実行、持っている:

while(shouldRun) 
{ 
    // Do some work .... 
    Thread.MemoryBarrier(); 
} 

を、別のスレッドは、falseからshouldRunを設定することもできます同期メカニズムを使用しないでください。

whileループの中でこの呼び出しを行うと、Thread.MemoryBarrier()を理解する限り、キャッシュされたバージョンのshouldRunがワークスレッドによって取得されなくなり、無限ループが発生するのを効果的に防止できます。

Thread.MemoryBarrierについての私の理解は正しいですか?shouldRun変数を設定できるスレッドがあれば(これは簡単に変更できません)、これはループが一旦停止することを保証するうえで妥当な方法ですか?shouldRunはどのスレッドでもfalseに設定されていますか?これは最新のを達成するために必要であれば、揮発性としてshouldRunの宣言は、メモリバリアを実行するので、この場合Thread.MemoryBarrier();

+0

は揮発そうであるように、ブールを宣言しません同じこと?私はThread.MemoryBarrierについては分からないので、コメントには答えません。例えばhttp://www.albahari.com/threading/part4.aspxAnother> "この問題を解決するより高度な方法は、_completeフィールドにvolatileキーワードを適用することです。volatileキーワードは、コンパイラにすべてのそのフィールドからの読み取り、およびそのフィールドへのすべての書き込み時の解放フェンス」を参照してください。よろしくお願いします。 –

+3

揮発性は非常に悪いものであり、使用しないでください。http://blogs.msdn.com/b/ericlippert/archive/2011/06/16/atomicity-volatility-and-immutability-are-different-part -three.aspx – Steven

+1

Lol、私はそれを知らなかった!元のC/asmプログラマとして、私はそれが良いことだと思った:)リンクをありがとう、私は見てみましょう。 –

答えて

26

条件付きループによって読み込まれる前に発生するループの本体に起こるた読み取りおよび書き込みのすべてをされています迫られていますこれはThread.MemoryBarrier()の正しい使用ですか?

いいえ1つのスレッドがの前により前にループを実行するとします。ループは、フラグのキャッシュされた値を使用して、一度は実行できます。 は、が正しいですか?確かに私には間違っているようです。私はループの最初の実行前にフラグを設定すると、ループは一度ではなくゼロ回実行されることが期待されます。

私の知る限りshouldRunのキャッシュされたバージョンを取得し、かつ効果的に起こってから無限ループを防止することから、私の仕事のスレッドを防ぐことができますwhileループ内でこのコールを持つ、Thread.MemoryBarrier()を理解するとして。 Thread.MemoryBarrierについての私の理解は正しいですか?

メモリバリアはプロセッサはバリアが実際それ後、および副であることが観察され前に、論理的にであるように、メモリアクセスを読み込みと書き込みのいずれかのreorderingsをしないことを保証しますその逆。

低ロックコードを実行しているときは、明示的なメモリバリアを導入するのではなく、フィールドを揮発性にする傾向があります。 "volatile" で、C#言語の機能です。危険でわかりにくい機能ですが、言語の機能です。これは、問題のフィールドが複数のスレッドにロックされずに使用されることをコードの読者に明確に伝えます。

これは、ループが一旦停止するようにするための合理的な方法ですshouldRunはどのスレッドでもfalseに設定されていますか?

一部の人々はそれが妥当であると考えます。私は非常に、非常に良い理由なしに私自身のコードでこれを行うつもりはありません。

一般的に、ローロック技術はパフォーマンス上の考慮事項によって正当化されます。そのような考慮事項は2つあります。

まず、競合するロックは非常に遅い可能性があります。ロック中にコードが実行されている間はブロックされます。競合が多すぎるためにパフォーマンスに問題がある場合は、最初に競合を解消することで問題を解決しようとします。私が競合を解消できない場合にのみ、私はローロック技術に行きます。

第2に、の未承諾のロックが遅すぎる可能性があります。ループで実行している「作業」が、たとえば200ナノ秒未満であれば、未然のロックをチェックするのに要する時間(約20 ns)は、作業に費やされる時間のかなりの部分です。その場合、私はあなたがループごとに多くの作業を行うことをお勧めします。ループが制御フラグの200 ns以内に設定されていることが本当に必要ですか?

極端なパフォーマンスのシナリオでのみ、競合していないロックをチェックするコストは、プログラムに費やす時間のかなりの部分であると想像しています。

また、もちろん、200ナノ秒ごとにメモリバリアを誘導している場合は、他の方法でパフォーマンスを損なう可能性もあります。プロセッサーは、があなたのためにこれらの移動メモリアクセス時間に最適化を行うことを望んでいます。あなたがそれらの最適化を絶えず捨て去るように強制するなら、あなたは潜在的な勝利を逃してしまいます。

2

private static volatile readonly bool shouldRun = true; 

の少ないパフォーマンスではなく、あまり安全変異体であります読んだり、そうする必要はないかもしれません。

3

あなたがしようとしていることによっては、これはおそらくあなたが期待したことをしません。 MSDNから、メモリバリア次のよう

は、メモリアクセスを同期: 現在のスレッドを実行するプロセッサは、メモリ 前メモリバリアへの呼び出しの呼び出しに従うメモリ アクセスした後に実行アクセスするように命令を並べ替えることができませんto MemoryBarrier。

これにより、メモリバリアコールの前に命令の並べ替えができなくなりますが、書き込みスレッドが実際に書き込みを実行する前にスレッドスケジューリングがループをもう一度繰り返す必要があると判断されません。それはロックまたは同期メカニズムではありません。ループ内の他の命令(変数初期化のような)がの前に再配置されるのを防ぐだけです。 shouldRunの値がチェックされています。

同様に、これを残しても無限ループは発生しません。いずれの場合も、すべての反復でshouldRunがチェックされます。ここには「キャッシュされた値」はありません。

+0

スレッドはshouldRunがfalseになるのを見て、特定の同期化が要求されない限り、フェンシングはまったく必要ありません。フィールド値は最終的に変更されることが観察されます。フェンシングは、正しい動作が発生するために必要な特定の順序付け/読み取りと書き込みが必要な場合にのみ問題になります。 –

7

私はあなたの理解は、このライン上にビットをオフになっていると信じて

私の知る限りThread.MemoryBarrier()は、whileループ内でこのコールを持つことのキャッシュされたバージョンを取得してから、私の仕事のスレッドを防ぐことができます理解してshouldRunを実行し、無限ループが起こることを効果的に防止します。

メモリバリアは、読み取り/書き込み命令に発注制約を強制する方法です。読み取り/書き込みリオーダリングの結果はキャッシングの外観を持つことができますが、メモリバリアは実際にはどのような方法でもキャッシュに影響を与えません。これは、単に読み書き命令が交差できないフェンスとして機能します。

これはすべて無限ループを防ぎません。どのようなメモリフェンスがやっていることは、このシナリオはshouldRunの値は

Wikipedia has a nice walk through on memory barrier that you may find useful

3

これは質問に対する直接の回答ではありません。しかし、それは上記の記事でよくカバーされているようです。明確に述べられていないように思われるのは、望ましい結果を達成する方法です。ちょうど意図したスレッド同期オブジェクトを使うことには何も問題は本当にありません:

private static readonly ManualResetEvent shutdownEvent = new ManualResetEvent(false); 

//Thread 1 - continue until event is signaled 
while(!shutodwnEvent.WaitOne(0)) 
{ 
    // Do some work .... 
    Thread.MemoryBarrier(); 
} 

//Thread 2 - signal the other thread 
shutdownEvent.Set(); 

他のアプローチは、変数にアクセスする際にロックステートメントを使用することです:

private static object syncRoot = new Object(); 
private static bool shouldRun = true; 

//Thread 1 
bool bContinue; 
lock(syncRoot) 
    bContinue = shouldRun; 

while(bContinue) 
{ 
    // Do some work .... 
    lock(syncRoot) 
     bContinue = shouldRun; 
} 

//Thread 2 
lock(syncRoot) 
    shouldRun = false; 
関連する問題

 関連する問題