2012-01-21 46 views
3

私たちはアプリケーションでTaskを使用しています。あるクラスでは、並列タスクで実行されている更新をトリガーしたいと考えています。呼び出しは次のようになります。SpinWait.SpinUntilに代わり

  Maintenance.RecievedMessage += new NotificationHandler(Maintenance_RecievedMessage); 
      Maintenance.checkLastXML = false; 
      Maintenance.NeedToUpdateFromCarrier(userId); 

     SpinWait.SpinUntil(() => isCompleted == true); 
     return true; 

だから我々はMaintenance.NeedToUpdateFromCarrier(USERID)トリガーされるイベントをフックアップ。 方法が実行されています。完全な方法は次のようになります。

private void Maintenance_RecievedMessage(IsCompleted changeargs) 
    { 
     isCompleted = true; 
    } 

だから我々はすぐにそれはそれを行うことをイベントをトリガーに行われますよう、NeedToUpdateFromCarrier方法を待っている、と私たちはイベントをキャッチし、真の、そしてthatsのにプロパティisCompleteを設定しますSpinWait.SpinUntil finnalyが完了したときに、私たちは続行します。

SpinWait.SpinUntilはCPUにとって非常に重いので、私は今この問題の代替ソリューションを探しています。

+0

このようにする理由がわからない場合は、フォーク/結合パターンを使用できませんか? – gjvdkamp

+0

どのように機能しますか?リンクがありますか?私の主な目標は、正しいものが真実であるのを待つこと、そして続けることです。 – Fore

+0

http://msdn.microsoft.com/en-us/magazine/cc163340.aspx#S7ただし、状況は正確ですか?中央のキューなどに書き込むタスクがたくさんありますか?あなたがしたいことではっきりしない。 – gjvdkamp

答えて

11

スピン待ちが適切であるときに理解することが重要です。それがあるケースはごくわずかです。スピン待機は、スレッドコンテキストの切り替えを最適化します。 WaitHandle.Wait()のような呼び出しは、何かを待つたびにスレッドをブロックしてプロセッサを生成します。オペレーティングシステムは、有用な作業を行うために他のスレッドが見つかったときにスレッドコンテキストスイッチを実行します。

スレッドコンテキストスイッチはかなり高価です。 yield-toスレッドがどこで実行されるか、スレッドが別のプロセスまたは保護リング(ドライバ)で実行されるときに余分なオーバーヘッドが発生するため、正確な数値はありません。 2000〜10,000サイクルのコストがかかります。

これはあまり成果のないCPUサイクルです。実際の作業が完了しないだけのオーバーヘッドです。 を知っている場合は、待機条件を満たすために常に20,000未満のサイクルがかかります。スレッド(スピン)を遅らせるだけで、高価なコンテキストの切り替えが不要になります。これは、Thread.Sleep()のような通常の種類の遅延ではなく、100%コアを焼く小さなループです。いくつかのスマートが投げ込まれているので、1つのコアだけでマシンを回転させるようなことは決してうまくいかないため、とにかく結果が得られます。

待ち状態が一貫して20,000サイクル以上かかる場合、これはうまく動作しません。今あなたは賢明な選択肢の反対側にいる、あなたはを行うは、そのような場合にしたい。何も成立しないときにCPUを焼くのを避けるためだけでなく、今度は収穫がそれを今度はにするよりも、の方が待ち時間が早くなる可能性があります。待機状態を設定するスレッドがジョブを終了するのに十分なCPUサイクルを得ることができるというオッズを増やすためです。

コードには多くの証拠があります。あなたはスピンする前に何かをするコードを明示的に求めています。また、完了を知らせるイベントハンドラが必要です。 Muchoコードを実行する必要があります。そして、最も説得力のあることに、あなたはCPUがたくさん燃えているのを見ています。 TaskMgr.exeの負荷の1%は約2,000万CPUサイクルです。

代わりに、AutoResetEventなどの待機可能なイベントを使用します。構造の変更が必要であることに注意してください。isCompletedはもうboolにはなりません。完了ハンドラでSet()を呼び出し、Wait()を呼び出してブロックします。

+0

この非常に詳細な回答ありがとうございます。これは、タスクを使用する際の最初の作業です。 dtbの解決策に同意しますか?または、どうすればそれを解決しますか、リトルコードスニペットで私を助けてください。 – Fore

+0

はい、dtbは同じ解決策を提示しました。私はちょうど*なぜ*それが適切な解決策に焦点を当てた。魚をする。 –

5

あなたはManualResetEventSlimを使用することができます。

var signal = new ManualResetEventSlim(); 

Maintenance.RecievedMessage += delegate { signal.Set(); }; 
Maintenance.checkLastXML = false; 
Maintenance.NeedToUpdateFromCarrier(userId); 

signal.Wait(); 
return true; 
+0

いいね、私はそれを試してみる。スピンウェイトのようなリソースが集中しているかどうか知っていますか? – Fore

+0

かなり軽量です。 'signal.Wait();を呼び出すスレッドは、シグナルが設定されるまでスリープします。 – dtb