为什么.NET计时器限制在15毫秒的分辨率? [英] Why are .NET timers limited to 15 ms resolution?

查看:351
本文介绍了为什么.NET计时器限制在15毫秒的分辨率?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请注意,我问的东西,将调用回调函数往往比使用类似 System.Threading.Timer 每隔15毫秒。我不要求有关使用类似 System.Diagnostics.Stopwatch 甚至 QueryPerformanceCounter的

另外,我读过的有关问题:

<一个href="http://stackoverflow.com/questions/448761/accurate-windows-timer-system-timers-timer-is-limited-to-15-msec">http://stackoverflow.com/questions/448761/accurate-windows-timer-system-timers-timer-is-limited-to-15-msec

<一个href="http://stackoverflow.com/questions/163022/high-resolution-timer-in-net">http://stackoverflow.com/questions/163022/high-resolution-timer-in-net

这两者都不提供一个有用的回答我的问题。

另外,推荐MSDN文章,实现一个不断更新的,高分辨率的时间提供用于Windows ,是关于时间的事情,而不是提供蜱的连续流。

随着中说。 。

有一个整体的很多不良信息在那里有关.NET Timer对象。例如, System.Timers.Timer的被标榜为用于服务器应用优化的高性能定时器。而 System.Threading.Timer 由于某种原因被认为是二等公民。传统的观点是, System.Threading.Timer 是围绕Windows的定时器队列定时器 System.Timers.Timer的完全是另一回事。

现实情况有很大不同。 System.Timers.Timer的就位于 System.Threading.Timer (只使用反射或ILDASM于薄部件的包装材料偷看里面的 System.Timers.Timer的键,你会看到引用 System.Threading.Timer ),并具有一些code,将提供自动线程同步,这样你就不必这样做。

System.Threading.Timer ,因为它原来的不是的一个包装定时器队列定时器。至少在2.0运行时,这是用来从.NET 2.0到.NET 3.5。几分钟的共享源代码CLI显示,运行时实现自己的定时器队列类似于定时器队列定时器,但从来没有实际调用Win32函数。

看来,.NET 4.0运行时也实现了自己的定时器队列。我的测试程序(见下文)提供了.NET 4.0下类似的结果,因为它确实.NET 3.5下。我已经创建了自己的托管包装的定时器队列定时器,证明我可以得到1毫秒的分辨率(与相当不错的准确度),所以我认为这是不可能的,我读的CLI源错误的。

我有两个问题:

首先,是什么原因导致运行时的执行定时器队列中是这么慢?我不能获得优于15毫秒分辨率和精确度似乎是在-1到30毫秒的范围内。也就是说,如果我要求的24毫秒,我会得到蜱任何地方,从23到54毫秒分开。我想我可以花更多的时间与CLI来源追查的答案,但认为这里的人可能知道。

第二,我知道这是很难回答,为什么不使用定时器队列定时器?我知道,.NET 1.x中不得不在Win9x,它不具有这些API运行,但他们自Windows 2000,而如果我没有记错对于.NET 2.0的最低要求已经存在了。难道是因为CLI不得不在非Windows盒中运行?

我的定时器的测试程序:

 使用系统;
使用System.Collections.Generic;
使用System.Diagnostics程序;
使用的System.Threading;

命名空间TimerTest
{
    类节目
    {
        const int的TickFrequency = 5;
        const int的TestDuration = 15000; //15秒

        静态无效的主要(字串[] args)
        {
            //创建一个列表来保存蜱倍
            //名单pre-分配到prevent列表大小调整
            //从放缓的考验。
            名单&LT;双&GT; tickTimes =新的名单,其中,双&GT;(2 * TestDuration / TickFrequency);

            //启动一个秒表,所以我们可以跟踪这一过程的时间。
            秒表经过= Stopwatch.StartNew();

            //创建节约花费的时间每个刻度的计时器
            定时器股票=新的定时器((S)=&GT;
                {
                    tickTimes.Add(Elapsed.ElapsedMilliseconds);
                },NULL,0,TickFrequency);

            //等待测试完成
            Thread.sleep代码(TestDuration);

            //摧毁计时器并停止秒表
            ticker.Dispose();
            Elapsed.Stop();

            //现在让我们来分析结果
            Console.WriteLine({0:N0}蜱在{1:N0}毫秒,tickTimes.Count,Elapsed.ElapsedMilliseconds);
            Console.WriteLine(平均滴答声的频率= {0:N2}毫秒,(双)Elapsed.ElapsedMilliseconds / tickTimes.Count);

            //计算分和指定的频率最大偏差
            双minDiff = double.MaxValue;
            双maxDiff = double.MinValue;
            的for(int i = 1; I&LT; tickTimes.Count ++ I)
            {
                双差异=(tickTimes [I]  -  tickTimes [I  -  1]) -  TickFrequency;
                minDiff = Math.Min(差异,minDiff);
                maxDiff = Math.Max​​(差异,maxDiff);
            }

            Console.WriteLine(最小差异= {0:N4}毫秒,minDiff);
            Console.WriteLine(最大差异= {0:N4}毫秒,maxDiff);

            Console.WriteLine(测试完成preSS输入。);
            到Console.ReadLine();
        }
    }
}
 

解决方案

也许这里链接的文档说明这一点。它有点干,所以我只能浏览它很快:)

引述介绍:

  

系统计时器分辨率决定   如何频繁的Windows执行两个   主要行动:

     
      
  • 更新计时器滴答   如果算一个完整的刻度已经过去。
  •   
  • 检查是否计划的计时器对象   已过期。
  •   
     

一个计时器滴答是经过一个概念   Windows用来跟踪的时间   白天和线程量子倍的时间。   默认情况下,时钟中断和   计时器刻度是相同的,但Windows   或应用程序可以改变时钟   中断期。

     

默认的定时器   在Windows 7上的分辨率是15.6   毫秒(ms)。有些应用程序   减少到1毫秒,从而降低了   在移动系统通过电池运行时间   高达25%的

原籍:<一href="http://download.microsoft.com/download/3/0/2/3027D574-C433-412A-A8B6-5E0A75D5B237/Timer-Resolution.docx">Timers,计时器分辨率,以及高效的code开发(DOCX)。

Note that I'm asking about something that will call a callback function more often than once every 15 ms using something like System.Threading.Timer. I'm not asking about how to accurately time a piece of code using something like System.Diagnostics.Stopwatch or even QueryPerformanceCounter.

Also, I've read the related questions:

http://stackoverflow.com/questions/448761/accurate-windows-timer-system-timers-timer-is-limited-to-15-msec

http://stackoverflow.com/questions/163022/high-resolution-timer-in-net

Neither of which supplies a useful answer to my question.

In addition, the recommended MSDN article, Implement a Continuously Updating, High-Resolution Time Provider for Windows, is about timing things rather than providing a continuous stream of ticks.

With that said. . .

There's a whole lot of bad information out there about the .NET timer objects. For example, System.Timers.Timer is billed as "a high performance timer optimized for server applications." And System.Threading.Timer is somehow considered a second class citizen. The conventional wisdom is that System.Threading.Timer is a wrapper around Windows Timer Queue Timers and that System.Timers.Timer is something else entirely.

The reality is much different. System.Timers.Timer is just a thin component wrapper around System.Threading.Timer (just use Reflector or ILDASM to peek inside System.Timers.Timer and you'll see the reference to System.Threading.Timer), and has some code that will provide automatic thread synchronization so you don't have to do it.

System.Threading.Timer, as it turns out is not a wrapper for the Timer Queue Timers. At least not in the 2.0 runtime, which was used from .NET 2.0 through .NET 3.5. A few minutes with the Shared Source CLI shows that the runtime implements its own timer queue that is similar to the Timer Queue Timers, but never actually calls the Win32 functions.

It appears that the .NET 4.0 runtime also implements its own timer queue. My test program (see below) provides similar results under .NET 4.0 as it does under .NET 3.5. I've created my own managed wrapper for the Timer Queue Timers and proved that I can get 1 ms resolution (with quite good accuracy), so I consider it unlikely that I'm reading the CLI source wrong.

I have two questions:

First, what causes the runtime's implementation of the timer queue to be so slow? I can't get better than 15 ms resolution, and accuracy seems to be in the range of -1 to +30 ms. That is, if I ask for 24 ms, I'll get ticks anywhere from 23 to 54 ms apart. I suppose I could spend some more time with the CLI source to track down the answer, but thought somebody here might know.

Second, and I realize that this is harder to answer, why not use the Timer Queue Timers? I realize that .NET 1.x had to run on Win9x, which didn't have those APIs, but they've existed since Windows 2000, which if I remember correctly was the minimum requirement for .NET 2.0. Is it because the CLI had to run on non-Windows boxes?

My timers test program:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

namespace TimerTest
{
    class Program
    {
        const int TickFrequency = 5;
        const int TestDuration = 15000;   // 15 seconds

        static void Main(string[] args)
        {
            // Create a list to hold the tick times
            // The list is pre-allocated to prevent list resizing
            // from slowing down the test.
            List<double> tickTimes = new List<double>(2 * TestDuration / TickFrequency);

            // Start a stopwatch so we can keep track of how long this takes.
            Stopwatch Elapsed = Stopwatch.StartNew();

            // Create a timer that saves the elapsed time at each tick
            Timer ticker = new Timer((s) =>
                {
                    tickTimes.Add(Elapsed.ElapsedMilliseconds);
                }, null, 0, TickFrequency);

            // Wait for the test to complete
            Thread.Sleep(TestDuration);

            // Destroy the timer and stop the stopwatch
            ticker.Dispose();
            Elapsed.Stop();

            // Now let's analyze the results
            Console.WriteLine("{0:N0} ticks in {1:N0} milliseconds", tickTimes.Count, Elapsed.ElapsedMilliseconds);
            Console.WriteLine("Average tick frequency = {0:N2} ms", (double)Elapsed.ElapsedMilliseconds / tickTimes.Count);

            // Compute min and max deviation from requested frequency
            double minDiff = double.MaxValue;
            double maxDiff = double.MinValue;
            for (int i = 1; i < tickTimes.Count; ++i)
            {
                double diff = (tickTimes[i] - tickTimes[i - 1]) - TickFrequency;
                minDiff = Math.Min(diff, minDiff);
                maxDiff = Math.Max(diff, maxDiff);
            }

            Console.WriteLine("min diff = {0:N4} ms", minDiff);
            Console.WriteLine("max diff = {0:N4} ms", maxDiff);

            Console.WriteLine("Test complete.  Press Enter.");
            Console.ReadLine();
        }
    }
}

解决方案

Perhaps the document linked here explains it a bit. It's kinda dry so I only browsed it quickly :)

Quoting the intro:

The system timer resolution determines how frequently Windows performs two main actions:

  • Update the timer tick count if a full tick has elapsed.
  • Check whether a scheduled timer object has expired.

A timer tick is a notion of elapsed time that Windows uses to track the time of day and thread quantum times. By default, the clock interrupt and timer tick are the same, but Windows or an application can change the clock interrupt period.

The default timer resolution on Windows 7 is 15.6 milliseconds (ms). Some applications reduce this to 1 ms, which reduces the battery run time on mobile systems by as much as 25 percent.

Originally from: Timers, Timer Resolution, and Development of Efficient Code (docx).

这篇关于为什么.NET计时器限制在15毫秒的分辨率?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆