3

I was under the impression that Thread.Sleep(x) is not precise and that all it will do is sleep the thread for at minimum xms. See here, here and here.

When sleeping for a very low amount of time, e.g. 1ms, it is expected that you will find that the thread occasionally sleeps for about 15ms. This is apparently due to clock interrupt rate which by default is 64 times per second.

I tried this a couple of years ago and indeed, I too experienced the 15ms resolution. However, I have just tried again and now I am seeing 1ms to 2ms resolution with very rarely > 2ms.

What has changed? Has .NET changed (I'm using 4.6 at the moment, don't remember what I used 2 years ago)? Perhaps it is the operating system which has changed? (I used and am still using AWS EC2 Windows Server, but perhaps there has been an update.)

My simple test program:

private static Stopwatch sw =new Stopwatch();
static void Main(string[] args)
{
    var timer = new Stopwatch();
    var results = new List<long>();
    for (int i = 0; i < 100000; i++)
    {
        timer.Restart();
        Thread.Sleep(1);
        results.Add(timer.ElapsedMilliseconds);
    }

    foreach (var item in results.Where(x => x > 1).ToList())
    {
        Console.WriteLine(item );
    }

    Console.ReadLine();

}
pookie
  • 3,796
  • 6
  • 49
  • 105
  • 2
    What else do you have going on your computer at the time of testing? That will likely effect timings. – Carcigenicate Aug 30 '18 at 21:37
  • @Carcigenicate Nothing at the moment - perhaps that is why I am seeing pretty high precision? – pookie Aug 30 '18 at 21:38
  • My favorite is when I see people trying to sleep for 1 microsecond... – Michael Dorgan Aug 30 '18 at 21:39
  • 1
    Search for `timebeginperiod` here on SO if you're not familiar with its affect on `Sleep` and others. – 500 - Internal Server Error Aug 30 '18 at 21:41
  • @500-InternalServerError `timebeginperiod` is mentioned a few times in the posts I've linked. I am asking if the precision of `Thread.Sleep` has changed as I am now getting high precision. I think @Carcigenicate has a good point though - perhaps I should test when I have a CPU intensive task running in the background. – pookie Aug 30 '18 at 21:48
  • https://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/ – user2864740 Aug 30 '18 at 21:49
  • *"A"* (as in, "any") CPU-intensive task won't inherently make Thread.Sleep have the behavior described. – user2864740 Aug 30 '18 at 21:50
  • Run powercfg.exe /energy from an elevated command prompt to find the evildoer. If you use Chrome then you don't have to look, freeware from a company that has a big stake in battery-powered devices. – Hans Passant Aug 30 '18 at 22:48

1 Answers1

4

The precision of Thread.Sleep (and the underlying Windows API Sleep function in kernel32) is dependent on the resolution of the system clock, which has a default tick rate of roughly 15 ms. Applications can request a higher resolution, in which case the highest requested resolution is used globally.

In this case case, you're likely seeing sub-15 ms resolution because something running on your system has requested a higher resolution.

If your application actually needs sub-15 ms sleeps, it can request a higher resolution via the native timeBeginPeriod function.

However, note that it is possible that the underlying timer device won't support your requested resolution, so it's not good practice to write code that relies on this behavior unless you're absolutely sure of the hardware your code will be running on.

user2864740
  • 60,010
  • 15
  • 145
  • 220
Collin Dauphinee
  • 13,664
  • 1
  • 40
  • 71
  • The native system calls in ntdll.dll are `NtQueryTimerResolution(PULONG MaximumTime, PULONG MinimumTime, PULONG CurrentTime)` and `NtSetTimerResolution(ULONG DesiredTime, BOOLEAN SetResolution, PULONG ActualTime)`, using 100 ns time units. Also, of the winmm.dll functions, note that [`timeGetTime`](https://learn.microsoft.com/en-gb/windows/desktop/api/timeapi/nf-timeapi-timegettime) is updated according to the real timer resolution, as opposed to `GetTickCount`, which seems to use a count that's hard-coded to update every 15.625 ms, regardless of the real resolution. – Eryk Sun Aug 31 '18 at 02:05
  • @CollinDauphinee Thanks. I have noticed that if I fire up a bunch of YouTube videos, I start to see more wild measurements where `Thread.Sleep(x)` sleeps longer than `x` about 10% of the time. – pookie Aug 31 '18 at 06:55