9

この質問を検索しましたが、答えが見つかりませんでした。重複があれば、喜んで閉じます。

私は現在、テクノロジーのパフォーマンス評価を行おうとしていますが、かなり信じられない結果がいくつか見られたので、いくつか実験することにしました。その中で、Stopwatch クラスが期待どおりの値を返すかどうかを試してみたかったのです。

Stopwatch sw = Stopwatch.StartNew();
Thread.Sleep(1);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);

この場合、15 ミリ秒の戻り値がほとんど見られました。DateTime の解像度がそこにあることは理解していますが、Thread.Sleep(1) はスレッドを 1 ミリ秒スリープ状態にすべきではありませんか? 私が使用しているシステムは、Stopwatch.IsHighResolution true を返し、.NET 4 で実行されています。

背景: このコードは完全かつ適切な形式であり、Aerospike db get リクエストでいくつかの数値を収集することを目的としています。DB は同じボックスにありません。クエリが途中であったときに sw.ElapsedMilliseconds を出力すると、ほとんどミリ秒未満の応答が表示されます。これは、ほとんどの場合、Java の同等のコードがはるかに信頼できる 5ms ~ 15ms の応答を返していることを考えると、少し疑わしいと思われます。Java コードは System.nanoTime() の違いを使用しています。私の C# コードのサブミリ応答とは、Console.WriteLine(sw.ElapsedMilliseconds) が 0 を出力することを意味します。

4

6 に答える 6

34

ストップウォッチ以外のタイマーは、クロック割り込みによってインクリメントされます。デフォルトでは、Windows では 1 秒あたり 64 回ティックします。または 15.625 ミリ秒。そのため、16 未満の Thread.Sleep() 引数では、探している遅延が得られません。少なくとも 15.625 の間隔が常に得られます。同様に、たとえば、Environment.TickCount または DateTime.Now を読み取って 16 ミリ秒未満待機すると、同じ値が返され、0 ミリ秒が経過したと見なされます。

小さな増分測定には常にストップウォッチを使用してください。異なる周波数ソースを使用します。その解像度は可変で、マザーボードのチップセットに依存します。しかし、マイクロ秒よりも優れていると信頼できます。Stopwatch.Frequency はレートを示します。

クロック割り込みレートは変更できます。timeBeginPeriod() をピンボークする必要があります。これにより、1 ミリ秒まで下げることができ、実際に Thread.Sleep(1) を正確にすることができます。これをしないのが最善です。これは非常に力に優しくありません。

于 2013-09-28T12:33:28.170 に答える
5

これは、Win32 APIThread.Sleepのマネージ ラッパーに過ぎないと思います。Sleepこの API 関数の解像度が低いことはよく知られています。ドキュメントには次のように書かれています。

この関数により、スレッドはタイム スライスの残りを放棄し、dwMilliseconds の値に基づく間隔で実行できなくなります。システム クロックは一定の速度で「カチカチ」音をたてます。dwMilliseconds がシステム クロックの分解能よりも小さい場合、スレッドは指定された時間より短い時間だけスリープする可能性があります。dwMilliseconds が 1 ティックより大きく 2 未満の場合、待機時間は 1 ティックから 2 ティックまでの間のいずれかになります。スリープ間隔の精度を上げるには、timeGetDevCaps 関数を呼び出して、サポートされている最小タイマー解像度を決定し、timeBeginPeriod 関数を呼び出してタイマー解像度を最小値に設定します。timeBeginPeriod を呼び出すときは注意してください。頻繁に呼び出すと、システム クロック、システムの電力使用量、およびスケジューラに大きな影響を与える可能性があります。timeBeginPeriod を呼び出すと、

スリープ間隔が経過すると、スレッドは実行可能になります。0 ミリ秒を指定すると、スレッドは残りのタイム スライスを放棄しますが、準備が整ったままになります。準備完了スレッドがすぐに実行されるとは限らないことに注意してください。その結果、スレッドは、スリープ間隔が経過した後しばらく実行されない場合があります。詳細については、スケジュールの優先度を参照してください。

timeBeginPeriodそのため、提案されているようにandを使用しtimeEndPeriodてタイマーの解像度を上げることができますが、それは実際にはお勧めできません。本当に短期間ブロックする必要がある場合は、より良い解決策を見つけることをお勧めします。たとえば、マルチメディア タイマー。

于 2013-09-28T12:31:51.600 に答える
2

はい、あなたが考え出したことは絶対に正しいです。

Thread.SleepNミリ秒単位でのリリースを保証するものではありません。ミリ秒Sleep前にリリースされないようにします。N

つまりThread.Sleep(1)、少なくとも 1 ミリ秒間スリープすることを意味します。オペレーティング システムは、特定の時間、特定のスレッドのタイム スライスをスケジュールしません。

スレッドは、指定された時間、オペレーティング システムによって実行されるようにスケジュールされません。このメソッドは、WaitSleepJoin を含むようにスレッドの状態を変更します。

MSDNから

于 2013-09-28T12:29:43.870 に答える
1

「スレッド スリープ解決」を検索すると、次の関連する SO の質問が表示されます。

コンセンサスは、この回答のように timeBegin/EndPeriod() を使用して解像度を設定しない限り、デフォルトで Sleep() に少なくとも 15 ミリ秒かかるということです。

于 2013-09-28T12:33:08.133 に答える
1

Thread.Sleep(n) は、少なくとも n ミリ秒以内に発生するタイムスライス (またはスレッド クォンタム) の数の間、現在のスレッドをブロックすることを意味します。タイムスライスの長さは、Windows のバージョンや種類、プロセッサによって異なり、通常は 15 ~ 30 ミリ秒です。これは、スレッドが n ミリ秒を超えてブロックされることがほぼ保証されていることを意味します。スレッドが正確に n ミリ秒後に再起動する可能性は、ほぼ不可能です。したがって、 Thread.Sleep はタイミングには無意味です。

于 2013-09-28T12:29:14.613 に答える
0

Thread.Sleep(1)スレッドを 1 ミリ秒スリープ状態にしますが、通常、その精度は得られません。

ただし、スレッドがスリープ状態になると、カーネルがそのスレッドをレディ キューに戻し、スレッドが実際に再び実行されるときに実行状態にするまでに時間がかかります。

これらの要因はすべて、プラットフォーム、CPU の数、その他のプロセスの数などによって異なります。RT 以外のオペレーティング システムでは、これに関する保証はありません。

于 2013-09-28T12:29:32.760 に答える