1. ホーム
  2. c#

スピンウェイトとスリープ待機。どちらを使うべき?

2023-09-15 11:23:25

質問

効率的なのは

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

タイムアウト10000msの場合

または

を使う方が効率的でしょうか? Thread.Sleep を使う方が効率的でしょうか? 例えば、以下のようなものです。 SleepWait 関数を使用します。

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

私は、1sec以上のタイムアウトについて話している場合、SpinWaitのすべての降伏は良い使用パターンではないかもしれないことを懸念しています?これは有効な仮定でしょうか?

どのアプローチが好きですか、そしてそれはなぜですか?また、さらに良いアプローチはありますか?


更新 - より具体的になる。

BlockingCollection が制限された容量に達したときにスリーピングスレッドをパルスさせる方法はありますか?私はむしろ、Marc Gravel が提案するように、ビジーウェイトを完全に回避します。

どのように解決するのですか?

この ベスト を実現するための何らかのメカニズムを持つことです。 を積極的に検出する を検出するメカニズムが必要です。 になる が真になるのを受動的にポーリングするのではなく); これは任意の種類の待ちハンドルかもしれませんし、あるいは TaskWait とか、あるいは event を購読して、自分自身を解き放つことができるのです。もちろん、そのような "何かが起こるまで待つ" を行う場合、それはつまり やはり 単に次の作業をさせるのと同じように効率的ではない をコールバックとして つまり、待つためにスレッドを使用する必要はないのです。 Task には ContinueWith を使うか、あるいは、その作業を event で行うこともできます。その event は、文脈にもよりますが、おそらく最もシンプルなアプローチでしょう。 Task は、タイムアウトを伴う待機とコールバックの両方のメカニズムを含む、ここで話しているほとんどすべてのものを既に提供しています。

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


コメントについて; 私がどのように "is it full" メカニズムをフックするかは以下の通りです。

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);
    }
}

で、"put back into the collection"のコードで。

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