C# Timer for Millisecond Waits

2019-01-27 09:30发布

问题:

Every n*x milliseconds I perform an action where n = 0, 1, 2, ...; x is some increment.

Example - every 25 milliseconds I perform a calculation.

This action can take fewer than x seconds for each increment. As a result, I need a way in C# to wait the remaining (x - actual_time) milliseconds.

Example - if the calculation only takes 20 milliseconds, I need to wait 5 more milliseconds before re-running the calculation.

Please advise.

Thanks, Kevin

回答1:

I need a way in C# to wait the remaining (x - actual_time) milliseconds.

I presume that is C# running on Windows.

And there is your problem. Windows is not a "realtime" operating system.

The best you can do if you need millisecond-grade timing precision is to set the thread priority of your thread extremely high, and then busy-wait while querying the high performance timer (Stopwatch).

You cannot yield to another thread; the other thread could run for as much as 16 milliseconds before the operating system context switches it, and of course unless you are the highest priority thread, you have no guarantee that control is coming back to you after those 16 milliseconds are up.

Now, setting thread priority high and then busy waiting is one of the most rude things you can possibly do; essentially you will be taking control of the user's machine and not allowing them to do anything else with it.

Therefore what I would do is abandon this course of action entirely. Either, (1) consider obtaining an operating system designed for realtime process control if that is in fact your application, rather than an operating system designed for multitasking a bunch of line-of-business applications. Or (2) abandon your requirement that the action happen exactly every 25 milliseconds. Just perform the calculation once and yield the remainder of your quantum to another thread. When you get control back, see if more than 25 ms has passed since you yielded; if it has not, yield again. If it has, start over and perform the calculation.



回答2:

That level of accuracy will be very difficult to achieve in a non real-time operating system like Windows. Your best bet might be to look into the multimedia timers.

The other .NET timers won't have the kind of resolution your need.



回答3:

At 25ms, you may be the wrong side of the resolution of your available timers in .Net.

However - as a general solution I'd probably attempt this a different way to your "do calculation..wait until 25ms has passed" approach.

A better way may well be to use a System.Timers.Timer, on a 25ms trigger, to trigger the calculation.

var timer = new Timer(25);
timer.Elapsed += (sender, eventArgs) =>
                     {
                         DoCalc();
                     };
timer.Start();

In the above example, a DoCalc method will be called every 25 ms (timer resolution issues notwithstanding). You would need to consider what to do if your calculation overran it's allotted time though. As it stands, the above code would allow a second calculation to start, even if the previous had not completed.



回答4:

This is a difficult one, and your options are fairly limited, as Eric Lippert and Matt Burland indicate. Basically, you can either

  • resort to using multimedia timers (google "multimedia timer component" or "winmm.dll"), which, although supporting time resolutions down to 0.500 ms, are no longer recommended as of Windows Vista, require Win32 interop and may bump up your CPU usage quite noticeably, or
  • come up with an approximated time slicing algorithm that will use the standard timer (whose resolution is usually 15.625 ms on multicore desktops), dynamically varying the timer interval upon each tick based on the difference of desired and actual time elapsed since the last timer tick (you can measure this fairly accurately using high resolution CPU performance counters, e.g. the Stopwatch class).

The latter solution will statistically give you a 40Hz timer in your sample use case, but you'll have significant jitter due to the low resolution of the timer you are using.

This is the tradeoff, the call is yours to make.



回答5:

Here's a high-precision timer I wrote. I get roughly <1ms avg precision on 25ms interval. But if Windows is busy it may come in late. It's fairly lean on CPU utilization. Feel free to use. It uses Sleep(1) when the next tick is more than 15ms away and then SpinUntil (which yields as needed) to keep CPU usage at bay. Uses .NET4 features.

Link: High Precision Timer



标签: c# timer