Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Thread.Sleep(1) takes longer than 1ms [duplicate]

Tags:

c#

.net

I searched this question but didn't see an answer. If it is a duplicate I'll gladly close it.

I am currently trying to do some performance evaluation on a technology and saw some rather unbelievable results so I decided to experiment some. In that I wanted to try and see if the Stopwatch class was returning what I expected.

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

In this case I was pretty much seeing a return value of 15ms. I understand the resolution of DateTime to be around there but shouldn't Thread.Sleep(1) sleep a thread for 1ms? The system I am on returns Stopwatch.IsHighResolution true and its running in .NET 4.

Background: This code in its complete and proper form is intended to gather some numbers on Aerospike db get requests. The DB is not on the same box. When I printed out the sw.ElapsedMilliseconds when a query was in the middle I'm seeing mostly sub millisecond responses and that sounds a little suspect considering my Java equivalent code is returning much more believable 5ms-15ms responses most of the time. The Java code is using the difference of System.nanoTime(). By submilli responses in my C# code I mean Console.WriteLine(sw.ElapsedMilliseconds) prints 0.

like image 421
Rig Avatar asked Sep 28 '13 12:09

Rig


3 Answers

Yes what you've figured out is absolutely correct.

Thread.Sleep doesn't guarantees to release at N milliseconds. It will make sure that Sleep will not release before N milliseconds.

In other words Thread.Sleep(1) means that Sleep for atleast 1 millisecond. Operating system will not schedule the time slice for specific thread for specific amount of time.

The thread will not be scheduled for execution by the operating system for the amount of time specified. This method changes the state of the thread to include WaitSleepJoin.

From Msdn

like image 107
Sriram Sakthivel Avatar answered Nov 15 '22 18:11

Sriram Sakthivel


Timers other than Stopwatch are incremented by the clock interrupt. Which by default ticks 64 times per second on Windows. Or 15.625 milliseconds. So a Thread.Sleep() argument less than 16 doesn't give you the delay you are looking for, you'll always get at least that 15.625 interval. Similarly, if you read, say, Environment.TickCount or DateTime.Now and wait less than 16 millisecond then you'll read the same value back and think 0 msec has passed.

Always use Stopwatch for small increment measurements, it uses a different frequency source. Its resolution is variable, it depends on the chipset on the motherboard. But you can rely on it being better than a microsecond. Stopwatch.Frequency gives you the rate.

The clock interrupt rate can be changed, you have to pinvoke timeBeginPeriod(). That can get you down to a single millisecond and actually make Thread.Sleep(1) accurate. Best not to do this, it is very power unfriendly.

like image 43
Hans Passant Avatar answered Nov 15 '22 18:11

Hans Passant


I expect that Thread.Sleep is nothing more than a managed wrapper around the Win32 Sleep API. It is well known that this API function has low resolution. The documentation says this:

This function causes a thread to relinquish the remainder of its time slice and become unrunnable for an interval based on the value of dwMilliseconds. The system clock "ticks" at a constant rate. If dwMilliseconds is less than the resolution of the system clock, the thread may sleep for less than the specified length of time. If dwMilliseconds is greater than one tick but less than two, the wait can be anywhere between one and two ticks, and so on. To increase the accuracy of the sleep interval, call the timeGetDevCaps function to determine the supported minimum timer resolution and the timeBeginPeriod function to set the timer resolution to its minimum. Use caution when calling timeBeginPeriod, as frequent calls can significantly affect the system clock, system power usage, and the scheduler. If you call timeBeginPeriod, call it one time early in the application and be sure to call the timeEndPeriod function at the very end of the application.

After the sleep interval has passed, the thread is ready to run. If you specify 0 milliseconds, the thread will relinquish the remainder of its time slice but remain ready. Note that a ready thread is not guaranteed to run immediately. Consequently, the thread may not run until some time after the sleep interval elapses. For more information, see Scheduling Priorities.

So, you could use timeBeginPeriod and timeEndPeriod as suggested to increase timer resolution, but that's really not to be recommended. If you really need to block for short periods of time then I suggest that you find a better solution. For instance a multimedia timer.

like image 34
David Heffernan Avatar answered Nov 15 '22 19:11

David Heffernan