请注意,我询问的是一个调用回调函数的方法,该方法使用类似的方法每15毫秒调用一次以上
System.Threading.Timer
. 我不是在问如何使用类似于
System.Diagnostics.Stopwatch
甚至
QueryPerformanceCounter
.
此外,我还阅读了相关问题:
Accurate Windows timer? System.Timers.Timer() is limited to 15 msec
High resolution timer in .NET
这两者都不能回答我的问题。
此外,推荐的msdn文章,
Implement a Continuously Updating, High-Resolution Time Provider for Windows
,是关于计时的事情,而不是提供连续的滴答声流。
就这么说。…
有很多关于.NET计时器对象的坏信息。例如,
System.Timers.Timer
被称为“针对服务器应用程序优化的高性能计时器”。
系统.线程.计时器
被认为是二等公民。传统的智慧是
系统.线程.计时器
是一个包裹在窗户周围的东西
Timer Queue Timers
那
系统计时器计时器
完全是另一回事。
现实情况大相径庭。
系统计时器计时器
只是一个很薄的组件包装
系统.线程.计时器
(只需使用反射镜或ILDASM窥视内部
系统计时器计时器
你会看到
系统.线程.计时器
,并且有一些代码可以提供自动线程同步,因此您不必这样做。
系统.线程.计时器
事实证明
不是
计时器队列计时器的包装。至少在从.NET 2.0到.NET 3.5使用的2.0运行时中没有。使用共享源CLI的几分钟时间表明,运行时实现了自己的计时器队列,该队列与计时器队列计时器类似,但从未实际调用win32函数。
.NET 4.0运行时似乎也实现了自己的计时器队列。我的测试程序(见下文)在.NET 4.0下提供了与在.NET 3.5下类似的结果。我已经为计时器队列计时器创建了自己的托管包装器,并证明了我可以获得1毫秒的分辨率(具有相当好的准确性),因此我认为我不太可能错误地读取CLI源。
我有两个问题:
首先,是什么导致运行时对计时器队列的实现如此缓慢?我无法获得超过15毫秒的分辨率,而且精度似乎在-1到+30毫秒的范围内。也就是说,如果我要求24毫秒,我将得到从23到54毫秒的任何间隔。我想我可以花更多的时间使用cli源代码来跟踪答案,但我想这里的人可能知道。
其次,我意识到这很难回答,为什么不使用计时器队列计时器呢?我意识到.NET 1.x必须在Win9x上运行,Win9x没有这些API,但它们自Windows 2000以来就存在了,如果我记得正确的话,这是.NET 2.0的最低要求。是因为cli必须在非Windows设备上运行吗?
我的计时器测试程序:
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();
}
}
}