System.Timer经过的事件似乎火迟到短的时间间隔在.net间隔、事件、时间、System

2023-09-02 02:03:21 作者:爱你‘无法自拔

我取样系统通过UART端口,然后记录在一个文件中的信息与时间戳(包括毫秒)。如果我品尝每隔1秒,数据回来预期...类似

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 back as expected...something like

下午1时52分45秒750的数据 下午1时52分45秒750的数据 下午1时52分45秒750的数据 下午1时52分46秒750的数据

1:52:45 PM 750 data 1:52:45 PM 750 data 1:52:45 PM 750 data 1:52:46 PM 750 data

然而,如果我减少定时器100毫秒的间隔中,将数据回来

However, if I decrease the interval of the timer to 100 ms, the data comes back

下午1时52分45秒531的数据 下午1时52分45秒640的数据 下午1时52分45秒750的数据 下午1时52分45秒859的数据 下午1时52分45秒968的数据 下午1时52分46秒78的数据。

1:52:45 PM 531 data 1:52:45 PM 640 data 1:52:45 PM 750 data 1:52:45 PM 859 data 1:52:45 PM 968 data 1:52:46 PM 78 data

总是有点晚了。

更糟糕较小的计时器时间间隔......我缺少什么吗?

It gets worse with smaller timer intervals...what am I missing here?

推荐答案

时钟漂移。非常典型的琐碎的定时器。这样做的原因是,它们通常使用的是睡眠功能实现。睡眠功能,始终保证睡眠至少指定的时间,但不能保证不睡觉比更多的时间,在实践中,总是积累了漂移。

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!
    }
}

这是一个粗糙但有效的定时器,这下一个是时钟我建立了一个WPF计时器,其中内置的定时器漂流遭受的例子。该定时器是一个复杂得多,它不养猪你的CPU。但clearily表明定时器具有典型的问题。

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.

这里使用的是内置计时器遭受漂移,但它调整的时间间隔,以补偿漂移的OnTimerTick

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;
}