4

私の .NET アプリケーションでは、一連のセンサー イベントを再生する必要があります。そこで、これらのイベントを発生させるスレッドを作成しました (通常は約 1 ~ 4 ミリ秒ごと)。このスレッドにループを実装しThread.Sleep(...)、イベント間でスレッドをスリープ状態にしていました。

基本的には次のようになります。

void RunThread() {
  var iter = GetAllEvents().GetEnumerator();
  if (!iter.MoveNext()) {
    return;
  }

  DateTime lastEventTime = iter.Current.Timestamp;
  FireEvent(iter.Current);

  while (iter.MoveNext()) {
    MyEvent nextEvent = iter.Current;

    int timeout = (int)(nextEvent.Timestamp - lastEventTime).TotalMilliseconds; 
    Thread.Sleep(timeout);

    FireEvent(nextEvent);
    lastEventTime = nextEvent.Timestamp;
  }
}

さて、私の問題はThread.Sleep()、指定されたタイムアウトを尊重する場合とそうでない場合があることです。

StopWatchスリープに実際にかかった時間のチェックを追加しました (上記のコードには表示されていません)。以下にいくつかの結果を示します。

Expected timeout of 1 ms but got 15 ms.
Expected timeout of 2 ms but got 13 ms.
Expected timeout of 3 ms but got 15 ms.
Expected timeout of 2 ms but got 13 ms.
Expected timeout of 1 ms but got 13 ms.
Expected timeout of 1 ms but got 15 ms.
Expected timeout of 2 ms but got 13 ms.
Expected timeout of 2 ms but got 40 ms.

なぜThread.Sleep()「ランダムに」振る舞うのですか?

ノート:

  • このスレッドの実行中、Windows タスク マネージャーは CPU 使用率を表示しません。
  • 動作はランダムに変化しますが、少なくとも数秒間持続します。たとえば、スレッドを開始すると、正常に実行されます。次回はずっと遅くなります。次回は数秒間高速で実行されますが、速度が低下するか、その逆になります。

更新:Sleepがどのように動作するかを示す擬似コードを次に示します。

bool ChooseRespectTimeout() {
  if (this.notYetChosen) {
    this.respectTimeout = ChooseRandomly()
    this.notYetChosen = false
    reset this.notYetChosen after random time period
  }
  return this.respectTimeout
}

void Sleep(int timeout) {
  if (ChooseRespectTimeout())
    Thread.Sleep(timeout)
  else
    Thread.Sleep(timeout * 10)
}
4

7 に答える 7

7

Thread.Sleep実際には、win32 Sleep関数を呼び出すだけで、そのドキュメントには不正確さが詳しく説明されています。

実際に行うことは、指定されたミリ秒程度Thread.Sleepの制御を放棄することです。スリープ機能の詳細は、多かれ少なかれ可能性があります。OSが制御を返すことができる速度は、システムクォンタムによって決定されます。これは10〜15ミリ秒です。したがって、実際には、最も近い量子に対してのみ正確である可能性があります。しかし、他の人が指摘しているように、CPUに大きな負荷がかかると、時間が長くなります。さらに、スレッドの優先度が他のすべてのスレッドよりも低い場合、時間がかからない可能性があります。Thread.Sleep

負荷に関しては、スイッチが発生する可能性があるときの負荷についてです。あなたは1msのタイムアウトについて話している(これは、システムのクォンタムまたはタイマーの精度のために、あなたが目撃したように、実際には約15です)。つまり、OSは実際には15〜30ミリ秒だけビジーである必要があります。忙しすぎて、将来のクォンタムまでスレッドに制御を戻すことができません。それはそれほど時間ではありません。優先度の高いものがその15〜40ミリ秒でCPUを必要とする場合、優先度の高いスレッドがタイムスライスを取得し、制御を放棄したスレッドを枯渇させる可能性があります。

Thread.Sleep(タイムアウト値が1より大きい場合)実際に意味するのは、スレッドが制御を取り戻すまで、少なくともタイムアウト値については、このスレッドの優先度がシステム内の他のすべてのスレッドよりも低いことをOSに通知する ことですタイミングメカニズムではなく、制御を放棄する手段です。彼らは本当に「Sleep」を「Yield」に名前変更する必要があります。Thread.Sleep

定期的に何かを行うものを設計したい場合は、タイマーを使用してください。本当に正確にする必要がある場合は、マルチメディアタイマーを使用してください。

于 2012-09-07T17:05:16.257 に答える
3

Windowsはリアルタイムオペレーティングシステムではありません。

スケジューラーは、特に別のスレッドがまだビジーである場合、スレッドを実行するために要求されたスリープ期間より長く待機する場合があります。

信頼性を高めたい場合は、マルチメディアタイマーを使用してみてください。ここに.NETラッパーがあります。

于 2012-09-07T15:40:30.097 に答える
2

プログラムが CPU の唯一のユーザーであるとは限りません。

他のプロセスは CPU のスライスを取得します -タイムアウト後に最善Thread.Sleepを尽くして再開しようとしますが、CPU がビジーの場合は待機する必要があります。

于 2012-09-07T15:38:15.247 に答える
2

Thread.Sleep少なくとも指定された間隔でスレッドがスリープすることのみを保証します。ブロックされた各スレッドにいつ CPU 時間を与えるかを決定するのは OS スケジューラであるため、正確性を保証するものではありません。

スレッドが特定の時点で再度実行することを要求できる場合、プリエンプティブ マルチタスクは不可能になります。

于 2012-09-07T15:38:26.147 に答える
2

Windows はリアルタイム OS ではないため、そのような保証はありません。基本的に、Thread.Sleep は少なくとも ミリ秒スリープしますが、特に非常に小さい値の場合はそれ以上になる可能性があります。

于 2012-09-07T15:38:36.607 に答える
2

Thread.Sleep()正確ではありません。あなたがそれに与える数字は、正確な睡眠時間ではなく、最小の睡眠時間です。さらに、Thread.Sleep には最小の粒度もあり、10ms 程度の IIRC だと思います。

より正確なタイミングが必要な場合は、代わりにタイマーを使用することをお勧めします。ただし、特に Windows と .NET は「リアルタイム」ではないため、ピンポイントの精度が得られないことに注意してください。

たとえば、ガベージ コレクターが作動すると、タイマーの処理に時間がかかる場合があります。

于 2012-09-07T15:38:36.643 に答える
0

最近これを指摘したのはあなただけではありません。XP以降、何かが変わったに違いありません。Sleep() は非常に信頼性が高く、実際に正確であり、それが役立つ間隔で、たとえば. 秒または分。他の人が指摘しているように、4msはSleep()を介して利用できるものから完全に外れています。

自分でテストしていませんが、最近のOSでは、何らかの形で余分な不確実性が忍び寄っているようです。

かなり長い期間にわたって、スリープ スレッドの優先順位を上げて、スリープ間隔の満了までに準備が整うとすぐにディスパッチされるようにする必要があります。

4 ミリ秒については、サイエンス フィクション (+1) で示唆されているように、マルチメディア タイマーを見てください。

于 2012-09-07T16:56:40.957 に答える