views:

708

answers:

8

I've come across a unit test that is failing intermittently because the time elapsed isn't what I expect it to be.

An example of what this test looks like is:

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();

TimeSpan oneSecond = new TimeSpan(0, 0, 1);

for(int i=0; i<3; i++)
{
    Thread.Sleep(oneSecond);
}

stopwatch.Stop();

Assert.GreaterOrEqual(stopwatch.ElapsedMilliseconds, 2999);

Most of the time this passes but it has failed on at least on one occasion failed because:

Expected: greater than or equal to 2999 But was: 2998

I don't understand how it could possibly be less than 3 seconds. Is there an accuracy issue with Thread.Sleep or maybe Stopwatch that I'm not aware of?

Just as an update to some of the questions below. The scenario that is being unit tested is a class that allow's one to call a method to perform some action and if it fails wait a second and recall that method. The test shown above is just an approximation of what is happening.

Say I wanted to call a method DoSomething()...but in the event of an exception being thrown by DoSomething() I want to be able to retry calling it up to a maximum of 3 times but wait 1 second between each attempt. The aim of the unit test, in this case, is to verify that when we requested 3 retries with 1 second waits between each retry that the total time taken is greater than 3 seconds.

+5  A: 

Thread.Sleep is not intended to be used for precision waking. Really, windows architecture itself is not intended for this kind of thing.

Spencer Ruport
+2  A: 

Thread.sleep is very innacurate. You shouldn't use it for those kind of time calculation, System.Timers.timer or System.Thread.Timer are much more accurate

Charlie
+7  A: 

Your thread is sharing CPU Time with other threads. The Sleep will end as soon as it is your turn again and the kernel notices the sleep time has elapsed, so it is not that accurate.

CPU load, process priorities, number of concurrent threads, even from other processes, will have effect upon it.

Havenard
but if it only ends as soon as the kernel notices the sleep time has elapsed, how is it possible for it to take less time than I expect? 3010ms is understandable and acceptable in my case it is stopping before reaching the time requested...albeit by 1-2ms
mezoid
The implementation probably sets a wake up time a few milliseconds before the time you specify. If it didn't it would *always* wake up later. By setting the time earlier it has better odds of getting somewhere close.
Spencer Ruport
@mezoid: I don't think PC architectures are made to have much time accuracy. Old PCs "ticks" only 18.2 times per secound, so clock and "tick count" used be updated only every 52ms. I beleave it must be much faster nowadays, but I'm still not sure how accurate it is.
Havenard
+1  A: 

In one application where I wanted to sleep for atleast x milliseconds, I used some code similar to:

public void Sleep(int milliseconds)
{
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();

    while (stopwatch.ElapsedMilliseconds < milliseconds)
    {
        int timeout = milliseconds - stopwatch.ElapsedMilliseconds;
        Thread.Sleep(timeout >= 0 ? timeout : 0);
    }

    stopwatch.Stop();
}

In response to how accurate Thread.Sleep is, it's not accurate at all. I think the resolution is somewhere around 10ms. It isn't guaranteed to do much of anything except 'approximately' this long.

Matthew Scharley
Why not just Thread.Sleep(milliseconds + 100)?
MusiGenesis
Do you have any reference you could point me to about the resolution? If it's definitely 10ms I could change 2999 to 2990.....
mezoid
There is no hard and fast resolution because of things other people have noted (such as processor load influencing it). Basically it comes down to whenever your thread gets priority again to start executing. Anecdotal evidence I've seen around SO in several places though has pointed to an average of around 10ms.
Matthew Scharley
http://stackoverflow.com/questions/1116249/manualresetevent-vs-thread-sleep/1116297#1116297 and other answers I've read that I can't find now.
Matthew Scharley
+2  A: 

Thread sleeping and timing/throttling are very different things, and should be treated appropriately. Sleeping a thread is a general task, allowing the system to give other threads and processes the chance to execute without being specific about it. On the other hand, throttling an application or scheduling tasks that need accurate timing should be performed with an explicit timer.

Keep in mind, if you need time-accurate processes or synchronization, you will have a hard time achieving that with a normal process in windows. You would need to utilize windows realtime priorities to successfully achieve accurate timing or throttling, as windows can sleep any thread at any time if it is preempted by another thread.

jrista
I agree, the intention for Thread.Sleep is about thread priorities and not to do with precise clock timing. Until we can have a Caesium processing clock in our PC's, unfortunately precise(absolute) timing will not be available. :Phttp://en.wikipedia.org/wiki/Caesium
Russell
Well, I would hope most people don't need atomic-clock-accurate timing. :P I think the clock in most CPU's is precise enough for the majority of applications beyond the measurement of atom vibrations. :D
jrista
+2  A: 

Came across the issue too, My Java sleep inaccuracy article refers to a demonstration of how OS thread priority directly affects your sleep accuracy.

Gili Nachum
Excellent article Gili. Thanks for that!
mezoid
+1  A: 

Quickly experimenting, I notice that a code fragment like...

do { Debug.WriteLine( DateTime.Now.TimeOfDay.TotalMilliseconds.ToString() ); } while ( 1 );

displays the same number multiple times, then jumps to a new number displayed multiple times, etc. The gap between these sets of numbers is consistently 15.625ms which I notice is 1000 / 64.

Looks like Windows timers have a granularity of 1/64th of a second. If you need better than that then I feel your pain, but that's the framework you have to fit within. (Windows isn't a hard real-time OS and doesn't claim to be).

Mark Gearing
A: 

Maybe you shoudn't rely on the time bias to find out if you where successfully. Better count your number of tries and evaluate against this:

int tries;

for(tries=0; tries<3; tries++)
{
    Thread.Sleep(oneSecond);
}

Assert.GreaterOrEqual(tries, 3);
Oliver