Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Thread.Sleep() is randomly imprecise

In my .NET application I have to replay a series of sensor events. So I created a thread that fires these events (usually about every 1 - 4 millisecond). I implemented a loop in this thread and used Thread.Sleep(...) to put the thread to sleep between the events.

Basically it looks like this:

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

Now, my problem is that Thread.Sleep() sometimes respects the specified timeout and sometimes it doesn't.

I added a check (using StopWatch, not visible in the code above) on how long the sleep actually took. Here are some results:

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.

Why does Thread.Sleep() behave that "randomly"?

Notes:

  • The Window task manager doesn't show any cpu usage while this thread runs.
  • The behavior changes randomly but persists for at least a couple of seconds. For example, one time when I start the thread, it does run fine. The next time it runs slow the whole time. The next time it runs fast for a couple of seconds, than slows down or the other way around.

Update: Here's some pseudo-code that shows how Sleep behaves:

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)
}
like image 714
Sebastian Krysmanski Avatar asked Dec 02 '22 21:12

Sebastian Krysmanski


1 Answers

Thread.Sleep really just calls win32 Sleep function, whose documentation details the inaccuracy.

What Thread.Sleep really does is give up control for about the specified milliseconds. The Sleep function details that could be more or less. The rate at which the OS can give back control is determined by the system quantum, which is 10-15 milliseconds. So, in reality Thread.Sleep can only be accurate to the nearest quantum. But, as others have pointed out, if the CPU is under heavy load that, time will be longer. Plus, if your thread has a lower priority than all other threads, it may not get time.

In terms of load, it's about load when the switch might occur. You're talking about a 1ms timeout (which is really about 15, as you've witnessed, due to the system quantum or timer accuracy) which means that the OS really only needs to be busy for 15-30 ms before it can be too busy to give back control to a thread until a future quanta. That's not a whole lot of time. If something of higher priority needs CPU in that 15-40ms, the higher-priority threads gets the time-slice, potentially starving the thread that gave up its control.

What Thread.Sleep really means (for timeout values larger than 1) is that it's telling the OS that this thread is lower in priority than every other thread in the system for at least the timeout value, until the thread gets control back. Thread.Sleep is not a timing mechanism, it's a means of relinquishing control. They should really rename "Sleep" to "Yield".

If you want to design something that periodically does something, use a timer. If you need to be really precise, use a multimedia timer.

like image 79
Peter Ritchie Avatar answered Dec 25 '22 10:12

Peter Ritchie