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

前端 未结 3 394
故里飘歌
故里飘歌 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.

    /// 
    /// Occurs when the timer interval has elapsed.
    /// 
    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;
    }
    

提交回复
热议问题