Reputation: 8394
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:
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)
}
Upvotes: 4
Views: 1320
Reputation: 35881
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.
Upvotes: 7
Reputation: 24857
You're not the only one to point this out recently. Something must have changed since XP. Sleep() was then pretty reliable, and indeed accurate, over intervals where it's useful, eg. seconds or minutes. 4ms is totally outside of what is available via Sleep(), as others have pointed out.
It seems that now, with later OS, some extra uncertainty has crept in somehow, though I haven't tested it myself.
Over usefully long periods, you should raise the priority of the sleeping thread so it will get dispatched as soon as it is made ready by the expiry of the sleep interval.
For 4ms, look at multimedia timers, as suggested by Science Fiction, (+1).
Upvotes: 0
Reputation: 3433
Windows is not a real time operating system.
The scheduler may wait longer than the requested sleep duration to execute a thread, especially if another thread is still busy.
Try use the Multimedia Timer if you want more reliability. There is a .NET wrapper here.
Upvotes: 3
Reputation: 93434
Thread.Sleep()
is not precise. The number you give it is the MINIMUM time to sleep, not the exact time to sleep. Further, Thread.Sleep also has a minimum granularity, which I think Is around 10ms IIRC.
If you need more precise timing, then you probably want to use a timer instead. However, be aware that Windows and .NET in particular are not "Real Time", so they cannot give you pinpoint accuracy.
For example, if the garbage collector kicks in, it may take longer to process timers.
Upvotes: 2
Reputation: 42627
Windows isn't a real-time OS, so you won't get those kinds of guarantees. Basically, Thread.Sleep will sleep for at least milliseconds, but it could be more, especially for very small values.
Upvotes: 2
Reputation: 437376
Thread.Sleep
only guarantees that the thread will sleep for at least the specified interval. It does not give accuracy guarantees -- indeed it cannot do so, because it is the OS scheduler that decides when each blocked thread should be given CPU time.
If threads could demand that they be run again in a specific point in time then preemptive multitasking would be impossible.
Upvotes: 2
Reputation: 499002
Your program is not guaranteed to be the sole user of a CPU.
Other processes get slices of the CPU - Thread.Sleep
will make a best attempt to resume after the timeout, but if the CPU is busy, it has to wait.
Upvotes: 2