System.Timer elapsed event seems to fire late for short intervals in .Net

前端 未结 3 360
故里飘歌
故里飘歌 2021-01-15 14:22

I am sampling a system over the UART port, and then logging the info in a file with a timestamp (including milliseconds). If I sample at 1 second intervals, the data comes

相关标签:
3条回答
  • 2021-01-15 15:06

    Clock drift. Very typical for trivial timers. The reason for this is that they are typically implemented using a sleep function. A sleep function is always guaranteed to sleep for at least the time specified but not guaranteed to not sleep for more time than that, in practice it's always accumulating a drift.

    There are ways to write timer that compensate for the drift and on average hit the target.

    One of my favorite timers are fixed step ones, that have a discreet tick. It's very simple and looks like this:

    var t = DateTime.Now + TimeSpan.FromSeconds(1);
    for (;;)
    {
        if (DateTime.Now >= t)
        {
            t += TimeSpan.FromSeconds(1); // Tick!
        }
    }
    

    That's a crude but effective timer, this following one is an example of a clock I built for a WPF timer where the built in timer was suffering from drifting. This timer is a lot more complex and it does not hog your CPU. But it clearily illustrates typical issues that timers have.

    The OnTimerTick here is using a built in timer that suffers from drift but it's adjusting the interval to compensate for the drift.

    /// <summary>
    /// Occurs when the timer interval has elapsed.
    /// </summary>
    public event EventHandler Tick;
    
    DispatcherTimer timer;
    
    public bool IsRunning { get { return timer.IsEnabled; } }
    
    long step, nextTick, n;
    
    public TimeSpan Elapsed { get { return new TimeSpan(n * step); } }
    
    public FixedStepDispatcherTimer(TimeSpan interval)
    {
        if (interval < TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException("interval");
        }
        this.timer = new DispatcherTimer();
        this.timer.Tick += new EventHandler(OnTimerTick);
        this.step = interval.Ticks;
    }
    
    TimeSpan GetTimerInterval()
    {
        var interval = nextTick - DateTime.Now.Ticks;
        if (interval > 0)
        {
            return new TimeSpan(interval);
        }
        return TimeSpan.Zero; // yield
    }
    
    void OnTimerTick(object sender, EventArgs e)
    {
        if (DateTime.Now.Ticks >= nextTick)
        {
            n++;
            if (Tick != null)
            {
                Tick(this, EventArgs.Empty);
            }
            nextTick += step;
        }
        var interval = GetTimerInterval();
        Trace.WriteLine(interval);
        timer.Interval = interval;
    }
    
    public void Reset()
    {
        n = 0;
        nextTick = DateTime.Now.Ticks;
    }
    
    public void Start()
    {
        var now = DateTime.Now.Ticks;
        nextTick = now + (step - (nextTick % step));
        timer.Interval = GetTimerInterval();
        timer.Start();
    }
    
    public void Stop()
    {
        timer.Stop();
        nextTick = DateTime.Now.Ticks % step;
    }
    
    0 讨论(0)
  • 2021-01-15 15:10

    The .NET timers don't have infinite resolution. My experiments with .NET 3.5 and .NET 4.0 show that you can expect, at best, 15 ms resolution with the .NET timer objects. My experience has been that with a timer interval of 100 ms, you can expect a tick typically in the 100 to 105 ms range, but I've seen a range as wide as 99 to 120 ms.

    For full details see http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=815.

    If you need better accuracy, you can use the Windows timers directly. I created a wrapper for the Timer Queue API at http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=817.

    0 讨论(0)
  • 2021-01-15 15:27

    Try using the Stopwatch class instead. Timer's are not meant to be accurate to that degree.

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