计时器所需时间比间隔多10毫秒。

3
我正在使用一个间隔为1秒的计时器。 但是在计时器的tick事件中,当我打印时间时,它总是62或65毫秒。 我不明白为什么会多花费10毫秒。
请有人来看一下。
这是我正在使用的代码:
static int _counter;
var _timer = new System.Timers.Timer(1000);
public Form1()
{
    InitializeComponent();           
    _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
    _timer.Start();            
}

private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
    Console.WriteLine(DateTime.Now.ToString("{hh:mm:ss.fff}"));          
    _counter++;
    if (_counter == 20)
        _timer.Stop();
}

这是输出结果:
{01:59:08.381}
{01:59:09.393}
{01:59:10.407}
{01:59:11.421}
{01:59:12.435}
{01:59:13.449}
{01:59:14.463}
{01:59:15.477}
{01:59:16.491}
{01:59:17.505}
{01:59:18.519}
{01:59:19.533}
{01:59:20.547}
{01:59:21.561}
{01:59:22.575}
{01:59:23.589}
{01:59:24.603}
{01:59:25.615}
{01:59:26.629}
{01:59:27.643}

2
您的问题描述和示例不一致。示例设置了1秒间隔,看起来显示的是1秒间隔,而不是50毫秒。 - simon
11个回答

15
你需要了解Windows不是一个实时操作系统。实时操作系统有定时器机制,允许系统对计时器启动的事件发生时间和相关开销做出硬性保证,并允许你指定当未满足截止时间时应该发生什么行为 - 例如如果前一次执行所需时间超过了间隔。
我认为当涉及较小间隔时,Windows计时器是“尽力而为”的。当间隔足够长时,你不会注意到你没有得到确切的请求间隔。随着接近计时器分辨率(计时器运行频率)的精度越来越高,你会看到作为间隔百分比的软件开销增加。实时系统特别注意减少软件开销,依赖更复杂和更快速的硬件解决方案。Windows计时器的确切频率取决于底层硬件提供的定时服务,因此可能因系统而异。
如果你需要实时需求 - 并且每50ms执行一次可能属于这一类 - 那么你可能需要考虑专门的硬件和/或实时操作系统。

1
先生,您并不了解用户要求的具体计时器。该计时器默认情况下不是很精确,与操作系统无关。Windows中还有其他更精确的计时器(例如多媒体计时器)。 - Pavlos Fragkiadoulakis
@PavlosFragkiadoulakis 哈哈 - 不要把定时器的粒度误认为是它的可预测性。如果您需要严格的实时保证,Windows 不是您应该选择的操作系统。虽然它可能足以播放音乐,但我真的怀疑您会在其上运行飞机。 - tvanfosson

7

这是由于系统时钟分辨率的限制。事件发生在指定时间之后的下一个系统时刻,因此您总会多得到几毫秒。


5
如果您需要更精确的计时器,可以使用Win32多媒体计时器,它是最准确的计时器(精确到1毫秒)。这里有一篇关于如何从C#中连接到它的文章,可以在CodeProject上查看。

4

首先,正如其他人所指出的那样,你将其设置为了1秒而不是50毫秒。

其次,Windows不是实时操作系统。所有计时器类都不是完全精确的。你所做的只是说明你希望等待至少这么长时间。当Windows开始处理tick消息并通知你计时器已经滴答一次时,需要一定的时间来完成所有操作。


你可以通过使用性能计数器在绑定到单个CPU核心的线程中来校准高分辨率计时器,从而使Windows表现更好。我认为我见过这样的操作,但没有编写相关代码,需要一些非常低级的技术(即可能是直接的机器码!)并且在这个领域存在着恶意固件漏洞。我认为最好的方法是停止担心时钟精度... - Donal Fellows
处理 A/D 外设、传感器设备和医疗设备时,计时精度非常重要。这非常,非常重要。但是,正如 @Donnie 指出的那样,Windows 不是实时操作系统。 如果您必须使用 Windows 操作系统,请尝试 WinCE 或 Windows 嵌入式系统。 - Stephen Furlani

2

System.Timers.Timer不是一个精确的定时器。特别是在系统负载较重时,它可能会有更大的延迟。

此外,在您的示例中为了获得更好的准确性,请将时间测量代码更改为使用Stopwatch类。

static int _counter;
        System.Timers.Timer _timer = new System.Timers.Timer(1000);
        Stopwatch sw;
        public Form1()
        {
            InitializeComponent();           
            _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
            _timer.Start();            
            sw = Stopwatch.StartNew();
        }

        void _timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            Console.WriteLine(sw.ElapsedMilliseconds);          
            _counter++;
            if (_counter == 20)
                _timer.Stop();            

            sw.Reset();
            sw.Start();
        }

2
请注意,在大多数语言中,sleep调用指定了进程唤醒的最短时间。在经过指定的时间后,进程被放入队列中,希望调度程序会激活它。但是这种激活有时可能会延迟。我不确定Timer类是否存在类似的问题,但我怀疑它可能会受到类似问题的影响。
您可以尝试提高进程的优先级以缩短增加的时间。

1

使用系统计时器总是会比请求的值多一点时间。这是由于系统中其他进程的开销造成的。


1
在我的系统上,它是14毫秒。经过谷歌搜索,差异归因于上下文线程切换延迟。这里有一篇关于高分辨率计时器的文章here

1

正如其他回答者所提到的,Windows不是实时操作系统。如果你必须使用Windows,尝试使用Win CE或Windows嵌入式。

-S!


1
时间的准确性可能取决于运行的进程数量。如果您有这个选项,我会逐个减少计算机上运行的进程数量,尤其是那些消耗大量CPU时间的进程,并检查时间是否得到改善。特别是浏览器、病毒扫描程序和后台运行的程序。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接