Why are .NET timers limited to 15 ms resolution?
请注意,我问的是使用类似
另外,我已经阅读了相关问题:
准确的Windows计时器? System.Timers.Timer()限制为15毫秒
.NET中的高分辨率计时器
这些都没有为我的问题提供有用的答案。
此外,推荐的MSDN文章"为Windows实现连续更新,高分辨率时间提供程序"是关于计时而不是提供连续的滴答流。
照这样说。 。 。
关于.NET计时器对象有很多不好的信息。例如,
现实情况大不相同。
似乎.NET 4.0运行时还实现了自己的计时器队列。我的测试程序(见下文)在.NET 4.0下提供与.NET 3.5相似的结果。我已经为Timer Queue Timers创建了自己的托管包装器并证明我可以获得1 ms的分辨率(具有相当好的准确性),所以我认为我不太可能错误地读取CLI源代码。
我有两个问题:
首先,是什么原因导致运行时计时器队列的实现如此缓慢?我的分辨率不能超过15毫秒,精度似乎在-1到+30毫秒的范围内。也就是说,如果我要求24毫秒,我会在23到54毫秒之间的任何地方得到滴答声。我想我可以花更多时间使用CLI源来追踪答案,但我想这里有人可能知道。
其次,我意识到这很难回答,为什么不使用定时器队列定时器?我意识到.NET 1.x必须在没有这些API的Win9x上运行,但它们自Windows 2000以来就存在,如果我记得正确的话,它是.NET 2.0的最低要求。是因为CLI必须在非Windows机器上运行吗?
我的计时器测试程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | 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(); } } } |
也许这里链接的文件解释了一下。它有点干,所以我很快就浏览了:)
引用介绍:
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.
最初来自:定时器,定时器分辨率和高效代码开发(docx)。
计时器分辨率由系统心跳给出。这通常默认为64次/秒,即15.625毫秒。但是,有些方法可以修改这些系统范围的设置,以便在较新的平台上实现低至1毫秒甚至0.5毫秒的定时器分辨率:
1.通过多媒体计时器界面获得1 ms分辨率:
多媒体定时器接口能够提供低至1毫秒的分辨率。
有关
怎么做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #define TARGET_RESOLUTION 1 // 1-millisecond target resolution TIMECAPS tc; UINT wTimerRes; if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) { // Error; application can't continue. } wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax); timeBeginPeriod(wTimerRes); // do your stuff here at approx. 1 ms timer resolution timeEndPeriod(wTimerRes); |
注意:此过程也适用于其他过程,并且所获得的分辨率适用于整个系统。任何流程要求的最高解决方案都将是活跃的,请注意后果。
2.分辨率为0.5毫秒:
您可以通过隐藏的API
NtSetTimerResolution由本机Windows NT库NTDLL.DLL导出。请参阅如何将计时器分辨率设置为0.5毫秒?在MSDN上。然而,真正可实现的解决方案由底层硬件决定。现代硬件支持0.5毫秒的分辨率。
更多详细信息可在Inside Windows NT高分辨率计时器中找到。支持的分辨率可以通过调用NtQueryTimerResolution()获得。
怎么做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #define STATUS_SUCCESS 0 #define STATUS_TIMER_RESOLUTION_NOT_SET 0xC0000245 // after loading NtSetTimerResolution from ntdll.dll: // The requested resolution in 100 ns units: ULONG DesiredResolution = 5000; // Note: The supported resolutions can be obtained by a call to NtQueryTimerResolution() ULONG CurrentResolution = 0; // 1. Requesting a higher resolution // Note: This call is similar to timeBeginPeriod. // However, it to to specify the resolution in 100 ns units. if (NtSetTimerResolution(DesiredResolution ,TRUE,&CurrentResolution) != STATUS_SUCCESS) { // The call has failed } printf("CurrentResolution [100 ns units]: %d ",CurrentResolution); // this will show 5000 on more modern platforms (0.5ms!) // do your stuff here at 0.5 ms timer resolution // 2. Releasing the requested resolution // Note: This call is similar to timeEndPeriod switch (NtSetTimerResolution(DesiredResolution ,FALSE,&CurrentResolution) { case STATUS_SUCCESS: printf("The current resolution has returned to %d [100 ns units] ",CurrentResolution); break; case STATUS_TIMER_RESOLUTION_NOT_SET: printf("The requested resolution was not set "); // the resolution can only return to a previous value by means of FALSE // when the current resolution was set by this application break; default: // The call has failed } |
注意:NtSetTImerResolution的功能基本上通过使用bool值
这里的所有重放都与系统定时器分辨率有关。但.net计时器不尊重它。作者自己注意到:
that the runtime implements its own timer queue that is similar to
the Timer Queue Timers, but never actually calls the Win32 functions.
Jan指出评论。
所以,上面的答案是很好的信息,但不直接与.net计时器相关,因此误导人:(
对两个作者问题的简短回答都是设计上的。他们为什么决定这样走?担心整个系统的性能?谁知道...
要不重复,请查看有关这两个问题的更多信息(以及实现精确问题的方法)
Jan的相关主题中的.net上的计时器。