问题
What .NET object (or technique) is the most precise at launching a thread every XXX milliseconds? What are the tradeoffs?
For example:
int maxDurationMs = 1000;
while (true)
{
DateTime dt = DateTime.UtcNow;
DoQuickStuff()
TimeSpan duration1 = DateTime.UtcNow - dt;
int sleepTime = maxDurationMs - duration1.Milliseconds;
if (sleepTime > 0)
System.Threading.Thread.Sleep(sleepTime);
}
or
// CPU Intensive, but fairly accurate
int maxDurationMs = 1000;
while (true)
{
DateTime dt = DateTime.UtcNow;
DoQuickStuff()
while (true)
{
if (dt.AddMilliseconds(maxDurationMs) >= DateTime.UtcNow)
break;
}
}
Alternate methods of doing the same thing, but with varying degrees of accuracy and tradeoffs (CPU, etc)
- System.Timer
- DispatchTimer
- System.Threading.Timer
- Thread.Join
- .NET 4.0 Task
- Thread.Sleep()
- Monitor.Wait(obj, timespan)
- Multimedia Timers (thanks Brian Gideon)
- Win32 High Resolution timers
- Something else?
回答1:
I have never actually used them myself, but Multimedia Timers are said to have the best resolution of any timer service in Windows. The .NET BCL does not have a wrapper for this timer service yet so you will have to do the P/Invoke calls yourself.
Another option might be to use Stopwatch
together with some standard Thread.Sleep
calls in a tight loop. I am not sure how much luck you would have with this approach, but it might be more accurate than a plain old Thread.Sleep
call by itself. I have never tried it, but anything is worth a shot I suppose.
I did some experiments and I discovered that changing the thread priority to ThreadPriority.Highest
made a considerable difference. It reduced the standard deviation of the interval by quite a bit on each technique I tried.
回答2:
Don't use DateTime: its accuracy is limited to around 16ms on most systems. (See Eric Lippert's blog)
The most accurate method would be to have a dedicated thread running a while loop with a System.Diagnostics.Stopwatch object to count the time.
Even with the most precise and accurate timer in existance, raising an event exactly every x milliseconds is no simple task given the unpredictability of CPU time slices: I suggest looking into how games do their main loop (achieving a stable 30fps with lag compensation, for instance). A good example is OpenTK's GameWindow, specifically the RaiseUpdateFrame method.
回答3:
If you want precise intervals Windows Timers are probably not what you need to be using, probably some sort of RTOS would be better suited for you.
From above link:
The Timer API was created to address problems with currently available timers... However, Windows timers are not as accurate as applications may require. Although Windows timer messages can be scheduled with millisecond accuracy, they seldom deliver that result because the accuracy of a Windows timer is dependent on the system clock and current activity. Because WM_TIMER messages are processed at a low priority, somewhat like WM_PAINT messages, they are often delayed while other messages are processed.
回答4:
In my window services periodic actions i use Monitor.Wait
because it releases a Thread and allows me to perform action without worrying about next "timer tick" before i finish.
With this I get +/- 1 ms precision. If everything goes right.
But if You need perfect precision that you can count on You shouldn't use .NET. Actually You shouldn't use Windows. There always is a possibility that Your process (or thread) will be postponed in execution.
回答5:
The basic implementation of System.Timers.Timer object skewed approx 120 MS and caused me to skip at least one second every minute.
I am able to use the following technique to get a timer accurate within 1ms on a 1 minute interval. Shorter intervals may not be able to achieve the same accuracy (plus the overhead to DoWork() plays a part in this efficiency)
public class SystemTimerTest
{
readonly System.Timers.Timer timerRecalcStatistics;
readonly System.Diagnostics.Stopwatch stopwatchForRecalcStatistics = new System.Diagnostics.Stopwatch();
public SystemTimerTest(TimeSpan range, DataOverwriteAction action)
{
int recalculateStatisticsEveryXMillseconds = 1000;
timerRecalcStatistics = new System.Timers.Timer(recalculateStatisticsEveryXMillseconds);
timerRecalcStatistics.AutoReset = true;
timerRecalcStatistics.Elapsed += new System.Timers.ElapsedEventHandler(TimerRecalcStatisticsElapsed);
timerRecalcStatistics.Interval = recalculateStatisticsEveryXMillseconds;
timerRecalcStatistics.Enabled = true;
this.maxRange = range;
this.hashRunningTotalDB = new HashRunningTotalDB(action);
this.hashesByDate = new HashesByDate(action);
this.dataOverwriteAction = action;
}
private void TimerRecalcStatisticsElapsed(object source, System.Timers.ElapsedEventArgs e)
{
stopwatchForRecalcStatistics.Start();
Console.WriteLine("The TimerRecalcStatisticsElapsed event was raised at {0}", e.SignalTime.ToString("o"));
// DO WORK HERE
stopwatchForRecalcStatistics.Stop();
double timeBuffer = GetInterval(IntervalTypeEnum.NearestSecond, e.SignalTime) - stopwatchForRecalcStatistics.ElapsedMilliseconds;
if (timeBuffer > 0)
timerRecalcStatistics.Interval = timeBuffer;
else
timerRecalcStatistics.Interval = 1;
stopwatchForRecalcStatistics.Reset();
timerRecalcStatistics.Enabled = true;
}
}
I wonder if this lost 1 to 120ms per 1 second cycle means the CPU isn't as efficient as it could be with this implementation.
来源:https://stackoverflow.com/questions/10609718/what-thread-sleep-method-is-most-precise-monitor-wait-vs-system-timer-vs-dispat