2012-03-15 21 views
52

は10000msスピンウェイトvsスリープ待ち。どちらを使うの?

または

のタイムアウトの

SpinWait.SpinUntil(() => myPredicate(), 10000) 

にそれが効率的です例えば同じ条件 ために、次のSleepWaitの線に沿って何かをThread.Sleepポーリングを使用することがより効率的です関数:

public bool SleepWait(int timeOut) 
{ 
    Stopwatch stopwatch = new Stopwatch(); 
    stopwatch.Start(); 
    while (!myPredicate() && stopwatch.ElapsedMilliseconds < timeOut) 
    { 
     Thread.Sleep(50) 
    } 
    return myPredicate() 
} 

私たちが1秒​​以上のタイムアウトについて話しているならば、SpinWaitのすべての利回りは良い使用パターンではないかもしれないと思いましたか?これは正当な仮定ですか?

あなたが好きですアプローチとなぜですか?もっと良いアプローチがありますか?


更新 - 複数の特定目指して:

は、それが有界容量に達したときにBlockingCollectionが眠っているスレッドをパルスにする方法はありますか? Marc Gravelが示唆するように、私はむしろ忙しい待ち時間を避けます。

答えて

39

最高アプローチは積極的なことが(むしろそれが真となったために受動的にポーリングよりも)真なりつつを検出にいくつかのメカニズムを持つことです。これは多分待機ハンドルの任意の種類、またはWaitと多分Task、またはあなた自身を引き離すためにするために購読することができeventである可能性があります。あなたは、スレッドを使用する必要はありません:あなたは、「何かが起こるまで待つ」のようなものを行う場合はもちろん、それは単に、コールバックとしてを行った作業の次のビットを持つ意味など、まだとして効率的ではありません待つ。 TaskにはContinueWithが含まれています。また、解雇時にeventで作業することもできます。文脈によっては、eventがおそらく最も簡単なアプローチです。しかし、Taskは、すでに「待ってタイムアウト」と「コールバック」の両方のメカニズムを含めて、ここで話しているほとんどすべてを提供しています。

そして、はい、10秒間スピンすることは素晴らしいではありません。あなたの現在のコードのようなものを使用したい場合、短い遅延を期待する理由があるが長いものを許可する必要がある場合は、おそらくSpinWaitを20msとし、それ以外の場合はSleepを使用しますか?


コメントを再入力してください。 「コレクションに戻す」のコードでは、と

private readonly object syncLock = new object(); 
public bool WaitUntilFull(int timeout) { 
    if(CollectionIsFull) return true; // I'm assuming we can call this safely 
    lock(syncLock) { 
     if(CollectionIsFull) return true; 
     return Monitor.Wait(syncLock, timeout); 
    } 
} 

:ここで私は、「それがいっぱいです」メカニズムをフックしたい方法です.NET 4 SpinWaitを行うCPUを集中的に

if(CollectionIsFull) { 
    lock(syncLock) { 
     if(CollectionIsFull) { // double-check with the lock 
      Monitor.PulseAll(syncLock); 
     } 
    } 
} 
+0

ありがとうございます、私はあなたの答えが好きです、これは確かにいいです。 BlockingCollectionを介していくつかのリソースの使用状況を追跡しているとします。それらは再利用のために再び利用可能になったときに使用され(そしてコレクションから削除され)、コレクションに戻されます。シャットダウンは、これらすべてがコレクションに戻った場合にのみ発生します。忙しい待っている以外のシャットダウンが進む(つまり、コレクションがいっぱいになった)ことを知らせる方法はありますか? – Anastasiosyal

+0

@Anastasiosyal私はコレクションをカプセル化し、完全なときにラッパーに何かをさせるでしょう。実際には、私はおそらくこのために 'Monitor'を使うでしょう(例を追加します) –

+0

BlockingQueueを継承する 'ObjectPool'クラスのデストラクタで、BlockingCollectionからすべてのオブジェクトを1つずつポップして破棄します。これはC#であるため、取り出されたオブジェクトの数がプールの深さと等しくなるまで、取得した参照をnilに設定します)。すべてのプールされたオブジェクトが破棄されたので、キューを廃棄してdtorから戻ってシャットダウンシーケンスを続行できます。ポーリング/ビジーウェイトは必要ありません!リークしたオブジェクトが最終的に例外(または何か)を発生させるように、テイクをタイムアウトにしたいかもしれません。 –

97

降伏前に10回反復回転する。しかし、これらの各サイクルの後には、すぐにに戻ってこない。代わりに、Thread.SpinWaitを呼び出して、CLR(本質的にOS)を介して一定の時間スピンします。この時間は最初は数10ナノ秒であるが、10回の反復が完了するまで各反復で2倍になる。これにより、システムが条件(コア数など)に応じて調整できる、スピン(CPU集中型)フェーズで費やされた合計時間の明瞭度/予測可能性が可能になります。 SpinWaitがスピンアップフェーズにとどまる時間が長すぎると、定期的にスリープして他のスレッドを進めることができます(詳細はJ. Albahari's blogを参照)。このプロセスは、SpinWaitが(実際にThread.YieldThread.Sleepを呼び出すことによって)それはすべてのスピンでそのタイムスライスを得た後の反復のセット数にCPU集約型の回転を制限し、だから... ...忙しいコアを維持するために

を保証されていますその資源消費を低下させる。また、ユーザーが単一のコアマシンを実行しているかどうかを検出し、そうであればすべてのサイクルで結果を出力します。

Thread.Sleepとスレッドはブロックされます。しかし、このプロセスは、CPUの点で上記のように高価ではありません。

+6

+1 SpinWaitとthread.Sleepの明瞭さのおかげで – Anastasiosyal

+0

これはAlbahariのウェブサイトからコピーされています。それは参照する必要があります! – georgiosd

+1

それはそのままコピーされていませんが、なぜ彼のブログを参照している彼のサイトの影響を受けました。 – MoonKnight

関連する問題