high frequency timing .NET

前端 未结 4 1437
猫巷女王i
猫巷女王i 2020-12-03 06:19

I\'m looking to create a high frequency callback thread. Essentially I need a function to execute at a regular high frequency (up to 100Hz) interval. I realize that windows

相关标签:
4条回答
  • 2020-12-03 06:37

    There is a lot of inaccurate information spread through the links and comments. Yes, by default the clock tick interrupt is 1/64 seconds = 15.625 msec on most machines but it can be changed. Also the reason that some machines appear to operate on another rate. The Windows multi-media api, available from winmm.dll lets you tinker with it.

    What you don't want to do is using a Stopwatch. You'll only get an accurate interval measurement out of it when you use it in a hot loop that constantly check if the interval has passed. Windows treats such a thread unkindly when its quantum expires, the thread won't be re-scheduled to run for a while when other threads compete for the processor. This effect is easy to miss since you don't typically debug your code with other processes actively running and burning cpu time.

    The function you want to use is timeSetEvent(), it provides a highly accurate timer that can go as low as 1 millisecond. It is self-correcting, reducing the interval if necessary (and possible) to catch up when the previous callback got delayed due to scheduling constraints. Beware however that it is difficult to use, the callback is made from a threadpool thread, similar to System.Threading.Timer, so be sure to use safe interlocking and take countermeasures that ensure that you won't get trouble from re-entrancy.

    A completely different approach is timeBeginPeriod(), it alters the clock interrupt rate. That has many side-effects, for one Thread.Sleep() becomes more accurate. Which tends to be the easier solution since you can make that synchronous. Just sleep for 1 msec and you'll get the interrupt rate. Some code to play with that demonstrates the way it works:

    using System;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    using System.Threading;
    
    class Program {
        static void Main(string[] args) {
            timeBeginPeriod(10);
            while (!Console.KeyAvailable) {
                var sw = Stopwatch.StartNew();
                for (int ix = 0; ix < 100; ++ix) Thread.Sleep(1);
                sw.Stop();
                Console.WriteLine("{0} msec", sw.ElapsedMilliseconds);
            }
            timeEndPeriod(10);
        }
    
        [DllImport("winmm.dll")]
        public static extern uint timeBeginPeriod(int msec);
        [DllImport("winmm.dll")]
        public static extern uint timeEndPeriod(int msec);
    }
    

    Output on my machine:

    1001 msec
    995 msec
    999 msec
    999 msec
    999 msec
    991 msec
    999 msec
    999 msec
    999 msec
    999 msec
    999 msec
    990 msec
    999 msec
    998 msec
    ...
    

    Do beware however of a problem with this approach, if another process on your machine has already lowered the clock interrupt rate below 10 msec then you will not get a second out of this code. That's very awkward to deal with, you can only make it truly safe by asking for a 1 msec rate and sleeping for 10. Don't run that on a battery operated machine. Do favor timeSetEvent().

    0 讨论(0)
  • 2020-12-03 06:37

    Thread.Sleep() does not seem to be as inaccurate as you may think at such small intervals, The following on Windows 8.1 x64 i7 Laptop .NET 4.0:

    using System;
    using System.Diagnostics;
    using System.Threading;
    
    namespace HighFrequency
    {
        class Program
        {
            static void Main(string[] args)
            {
                var count = 0;
                var stopwatch = new Stopwatch();
                stopwatch.Start();
                while(count <= 1000)
                {
                    Thread.Sleep(1);
                    count++;
                }
                stopwatch.Stop();
                Console.WriteLine("C# .NET 4.0 avg. {0}", stopwatch.Elapsed.TotalSeconds / count);
            }
        }
    }
    

    Output:

    C# .NET 4.0 avg. 0.00197391928071928

    So just under 2ms interval! I guess if there was more contention that could rise pretty quickly, though I do have a number of apps open, just nothing grinding away.

    At sleeping for 10ms i.e. 100HZ, got an average of 0.0109... pretty much spot on!

    0 讨论(0)
  • 2020-12-03 06:41

    You might want to try the StopWatch, which uses the same hi-performance timers used by DirectX.

    0 讨论(0)
  • 2020-12-03 06:57

    Note that part of the reason why 15ms is the effective minimum Sleep time is that it can take that order of magnitude to actually swap your process out, swap another one in, let it have some time to do anything, swap it out and swap yours in again. Assuming you have multiple cores, dedicating one to handingly your 100Hz updater is probably best, especially if you know when you'd like to skip a few cycles for a manually generated garbage collection, and/or use C/C++ as suggested.

    0 讨论(0)
提交回复
热议问题