web-dev-qa-db-ja.com

Thread.Sleep(0):通常の動作は何ですか?

私の理解では、Thread.Sleep(0)はOSのコンテキストスイッチを強制します。

CPU時間を受け取る前に、アプリケーションで経過できる最大時間はどれくらいかを確認したかったのです。

そこで、whileループ(c#)でThread.Sleep(0)を実行し、各呼び出しの間に経過する時間を計算するアプリケーションを作成しました。

このアプリケーションが2コアテストPCで実行されている唯一のアプリケーションである場合、最大観測時間は1ミリ秒未満(平均0.9マイクロ秒)であり、使用可能なすべてのCPU(100%)を使用します。

CPU充填ダミーアプリケーション(すべて同じ優先度)に沿って実行すると、最大時間は約25ミリ秒、平均時間は20ミリ秒です。期待どおりに動作します。そして、時間は非常に安定しています。

CPU時間を取得すると、処理を行う人にすぐに制御が戻り、ホットポテトゲームのようになります(CPU使用率は0%に低下します)。他に実行中のアプリケーションがない場合、コントロールはすぐに戻ります。

この動作を考えると、このアプリケーションが実際のアプリケーションを実行しているコンピューターに与える影響は最小限であると予想しました。 (そして、そこで実行されているアプリケーションで見られると期待できる実際の「待ち時間」を私に与えるために)。しかし、驚いたことに、この特定のシステムのパフォーマンスに(観察可能な方法で)悪影響を及ぼしました。

Thread.Sleep(0)に関する重要なポイントが欠けていますか?

参考までに、このアプリケーションのコードを次に示します。

private bool _running = true;
private readonly Stopwatch _timer = new Stopwatch();

private double _maxTime;
private long _count;
private double _average;
private double _current;

public Form1()
{
    InitializeComponent();
    Thread t = new Thread(Run);
    t.Start();
}

public void Run()
{
    while(_running)
    {
        _timer.Start();
        Thread.Sleep(0);
        _timer.Stop();

        _current = _timer.Elapsed.TotalMilliseconds;
        _timer.Reset();
        _count++;

        _average = _average*((_count - 1.0)/_count) + _current*(1.0/_count);
        if(_current>_maxTime)
        {
            _maxTime = _current;
        }
    }
}

わかりやすくするために編集(アプリケーションの目的):現在、約300ミリ秒ごとにいくつかの入力に反応する必要があるソフトリアルタイムマルチスレッドアプリケーション(アプリケーションのグループ)を実行していますが、時々(1%未満の時間で)いくつかの締め切りを逃し、私は現在その数を改善しようとしています。

同じマシン上の他のプロセスによって引き起こされる現在の変動性を確認したかったのです。上記のアプリケーションをこの半リアルタイムマシンに適合させることにより、観察された最大時間がシステムによって引き起こされる変動性を教えてくれるのは難しいです。 I.E. 300msありますが、スレッドがCPU時間を取得するまでの最大観測時間は50msであるため、パフォーマンスを向上させるには、処理時間を最大250msに設定する必要があります(すでに50ms遅れている可能性があるため)。

24
Benoittr

forceコンテキストスイッチではなく、Sleep(1)のみがそれを行います。ただし、実行する準備ができているプロセスから他のスレッドがあり、優先度が高い場合、Sleep(0)はプロセッサを生成し、実行させます。これは、Sleep(0)を呼び出す無限ループを実行することで確認できます。これにより、1つのコアで100%のCPUサイクルが消費されます。なぜあなたがこの行動を観察しないのか理解できません。

システムの応答性を維持するための最良の方法は、スレッドに低い優先度を与えることです。

33
Hans Passant

以前のプロジェクトでこのバグに噛まれました。優先キュー内のメッセージをチェックして新しいメッセージを探すスレッドを実行していました。新しいメッセージが見つからなかった場合は、メッセージがキューに追加されるまでスレッドをスリープ状態にして、再度確認するためにスレッドをウェイクアップする必要がありました。

単純に、Thread.Sleep(0)によってスレッドが再びウェイクアップするまでスリープ状態になると仮定すると、メッセージが着信し始めると、アプリが異常な量のCPUを消費していることがわかりました。

考えられる原因を数日間調査した後、 このリンク。 からの情報が見つかりました。簡単な修正はThread.Sleep(1)を使用することでした。リンクには、違いの理由に関する詳細があり、下部に小さなテストアプリがあり、2つのオプション間のパフォーマンスがどうなるかを示しています。

6
Eric Olsson

私の理解では、Thread.Sleep(0)はスレッドコンテキストスイッチをforceせず、タスクスケジューラにあなたが諦めても構わないと思っていることを通知するだけです。実行を待機している他のスレッドがある場合は、残りのタイムスライス。

Sleep(0)の周りのループは、CPU時間をかみ砕いており、他のアプリケーション(およびラップトップのバッテリー寿命!)に悪影響を及ぼします。 Sleep(0)は「他のすべてを最初に実行させる」という意味ではないため、ループは実行時間を他のプロセスと競合します。

ゼロ以外の待機時間をSleep()に渡すと、実際にはこのスレッドが最小限の時間だけ脇に置かれるため、他のアプリにとってはわずかに優れています。ただし、これは、影響が最小限のバックグラウンドスレッドを実装する方法ではありません。

フォアグラウンドアプリケーションへの影響を最小限に抑えてCPUバウンドのバックグラウンドスレッドを実行する最良の方法は、スレッドの優先度を通常よりも低くすることです。これにより、スケジューラーは最初にすべての通常の優先度のスレッドを実行し、他に利用可能な時間があれば、優先度の低いスレッドを実行するように指示されます。これの副作用は、CPUの飽和度によっては、優先度の低いスレッドが比較的長い時間(秒)にわたって実行時間をまったく取得しない場合があることです。

4
dthorpe

最も可能性の高い原因は、プログラムが効率的な方法で命令を読み取ることを許可していないことです。

Sleep(0)を呼び出すと、コードは1サイクル中断され、次に使用可能なスロットにスケジュールされます。ただし、このコンテキスト切り替えは無料ではありません。保存/ロードするレジスタがたくさんあり、ディスクから読み取る命令のシーケンスが異なると、キャッシュミスがかなり発生する可能性があります。ほとんどの場合、これがアプリケーションに大きな影響を与えるとは想像できませんが、リアルタイムシステムまたは同様の集中的なシステムで作業している場合は、可能性があります。

3
MikeD

他のプログラムによる影響もCPUバウンドのみであることに気づいたと思います。したがって、これは、OSスケジューラを考慮せずに実行する3番目のプログラムでもあります。

影響を受けていないプログラムは、プログラムの実行を許可するためにOSによって停止されており、プロセスをスワップアウトしてからロードしてから再度アンロードするためのコンテキストヒットがあります。

0
Simeon Pilgrim