System.Timer已经过去的事件似乎在.Net中以较短的间隔开始

时间:2011-03-10 20:04:42

标签: .net timer

我正在通过UART端口对系统进行采样,然后将信息记录在带有时间戳(包括毫秒)的文件中。如果我以1秒的间隔进行采样,数据会按预期返回......类似于

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 ms,则数据会返回

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

总是有点晚。

定时器间隔越小,情况越糟......我在这里缺少什么?

4 个答案:

答案 0 :(得分:3)

时钟漂移。非常典型的琐碎计时器。这样做的原因是它们通常使用睡眠功能来实现。睡眠功能总是保证至少在指定的时间内睡眠,但不能保证睡眠时间不超过这个时间,实际上它总是会累积漂移。

有一些方法可以编写定时器来补偿漂移并平均击中目标。

我最喜欢的一个定时器是固定步骤,有一个谨慎的滴答声。它很简单,看起来像这样:

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

这是一个原始但有效的计时器,以下是我为WPF计时器构建的时钟示例,其中内置计时器正在遭受漂移。这个计时器要复杂得多,不会占用你的CPU。但它清楚地说明了计时器的典型问题。

这里的OnTimerTick使用的内置定时器会受到漂移的影响,但它正在调整间隔以补偿漂移。

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

答案 1 :(得分:0)

请尝试使用Stopwatch课程。计时器并不意味着在那个程度上是准确的。

答案 2 :(得分:0)

.NET计时器没有无限分辨率。我使用.NET 3.5和.NET 4.0进行的实验表明,使用.NET计时器对象,最多只能达到15 ms的分辨率。我的经验是,如果定时器间隔为100 ms,您可以预期通常在100到105 ms范围内打勾,但我看到的范围宽达99到120 ms。

有关详细信息,请参阅http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=815

如果您需要更高的准确度,可以直接使用Windows计时器。我在http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=817创建了一个Timer Queue API的包装器。

答案 3 :(得分:0)

Windows 多媒体计时器可以实现更高的计时分辨率。请参阅此处了解 Windows 多媒体计时器包装器和示例用法 https://stackoverflow.com/a/67060100/4856020