高分辨率定时器

我想要一个约5毫秒分辨率的计时器。 但是.Net中当前的Timer具有大约50ms的分辨率。 我找不到任何可以创build高分辨率定时器的工作解决scheme,尽pipe有人声称可以在C#中完成。

关于OP特别询问定期触发事件的Timer类的信息。 我已经修改了这个答案,我的旧答案低于横向规则。

我用Timer类testing了下面的代码,好像它至less可以在我的机器上的14 – 15毫秒范围内。 自己尝试一下,看看你是否可以重现这一点。 因此,小于50毫秒的响应时间是可能的,但不能精确到1毫秒。

 using System; using System.Timers; using System.Diagnostics; public static class Test { public static void Main(String[] args) { Timer timer = new Timer(); timer.Interval = 1; timer.Enabled = true; Stopwatch sw = Stopwatch.StartNew(); long start = 0; long end = sw.ElapsedMilliseconds; timer.Elapsed += (o, e) => { start = end; end = sw.ElapsedMilliseconds; Console.WriteLine("{0} milliseconds passed", end - start); }; Console.ReadLine(); } } 

注意:以下是我的旧回答,当时我想OP是在谈论计时的事情。 以下仅仅是有用的信息,关于事物的持续时间的计时,但是不提供任何定期触发事件的方式。 为此, Timer类是必需的。

尝试在System.Diagnostics使用Stopwatch类: http : //msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx

你可以通过它的IsHighResolution字段查询它是否是高分辨率。 此外,您可以检查秒表的确切分辨率:

 int resolution = 1E9 / Stopwatch.Frequency; Console.WriteLine("The minimum measurable time on this system is: {0} nanoseconds", resolution); 

如果你担心这是从哪里来的,文档似乎意味着它实际上在内部调用较低级别的Win32函数:

秒表类帮助处理托pipe代码中与时间有关的性能计数器。 具体而言,频率字段和GetTimestamp方法可用于取代非托pipeWin32 API QueryPerformanceFrequency和QueryPerformanceCounter。

那这个呢?

 public class HiResTimer { private bool isPerfCounterSupported = false; private Int64 frequency = 0; // Windows CE native library with QueryPerformanceCounter(). private const string lib = "coredll.dll"; [DllImport(lib)] private static extern int QueryPerformanceCounter(ref Int64 count); [DllImport(lib)] private static extern int QueryPerformanceFrequency(ref Int64 frequency); public HiResTimer() { // Query the high-resolution timer only if it is supported. // A returned frequency of 1000 typically indicates that it is not // supported and is emulated by the OS using the same value that is // returned by Environment.TickCount. // A return value of 0 indicates that the performance counter is // not supported. int returnVal = QueryPerformanceFrequency(ref frequency); if (returnVal != 0 && frequency != 1000) { // The performance counter is supported. isPerfCounterSupported = true; } else { // The performance counter is not supported. Use // Environment.TickCount instead. frequency = 1000; } } public Int64 Frequency { get { return frequency; } } public Int64 Value { get { Int64 tickCount = 0; if (isPerfCounterSupported) { // Get the value here if the counter is supported. QueryPerformanceCounter(ref tickCount); return tickCount; } else { // Otherwise, use Environment.TickCount. return (Int64)Environment.TickCount; } } } static void Main() { HiResTimer timer = new HiResTimer(); // This example shows how to use the high-resolution counter to // time an operation. // Get counter value before the operation starts. Int64 counterAtStart = timer.Value; // Perform an operation that takes a measureable amount of time. for (int count = 0; count < 10000; count++) { count++; count--; } // Get counter value when the operation ends. Int64 counterAtEnd = timer.Value; // Get time elapsed in tenths of a millisecond. Int64 timeElapsedInTicks = counterAtEnd - counterAtStart; Int64 timeElapseInTenthsOfMilliseconds = (timeElapsedInTicks * 10000) / timer.Frequency; MessageBox.Show("Time Spent in operation (tenths of ms) " + timeElapseInTenthsOfMilliseconds + "\nCounter Value At Start: " + counterAtStart + "\nCounter Value At End : " + counterAtEnd + "\nCounter Frequency : " + timer.Frequency); } } 

我在以下博客中find了解决此问题的方法: http ://web.archive.org/web/20110910100053/http: //www.indigo79.net/archives/27#comment-255

它告诉你如何使用多媒体定时器有一个高频率的定时器。 这对我来说工作得很好!

系统时钟以固定速率“ 滴答 ”。 为了提高定时器相关函数* s的准确性,请调用* * timeGetDevCaps * ,它决定了支持的最小定时器分辨率。 然后调用timeBeginPeriod将定时器分辨率设置为最小值。

注意:通过调用timeBeginPeriod,其他与定时器相关的function会受到显着影响,如系统时钟,系统电源使用情况和调度程序。 因此用timeBeginPeriod 开始你的应用程序 ,并用timeEndPeriod结束它

您可以使用本文所述的QueryPerformanceCounter()和QueryPerformanceTimer()。

这是一个基于StopWatch计时器的实现
https://gist.github.com/DraTeots/436019368d32007284f8a12f1ba0f545

  1. 它适用于所有平台,并且在StopWatch.IsHighPrecision == true情况下具有高精度

  2. 它的Elapsed事件保证不重叠(这可能很重要,因为事件处理程序中的状态更改可能不受multithreading访问的保护)

以下是如何使用它:

 Console.WriteLine($"IsHighResolution = {HighResolutionTimer.IsHighResolution}"); Console.WriteLine($"Tick time length = {HighResolutionTimer.TickLength} [ms]"); var timer = new HighResolutionTimer(0.5f); // UseHighPriorityThread = true, sets the execution thread // to ThreadPriority.Highest. It doesn't provide any precision gain // in most of the cases and may do things worse for other threads. // It is suggested to do some studies before leaving it true timer.UseHighPriorityThread = false; timer.Elapsed += (s, e) => { /*... e.Delay*/ }; // The call back with real delay info timer.Start(); timer.Stop(); // by default Stop waits for thread.Join() // which, if called not from Elapsed subscribers, // would mean that all Elapsed subscribers // are finished when the Stop function exits timer.Stop(joinThread:false) // Use if you don't care and don't want to wait 

这是一个基准(和一个生动的例子):
https://gist.github.com/DraTeots/5f454968ae84122b526651ad2d6ef2a3

在Windows 10上将计时器设置为0.5毫秒的结果: 在这里输入图像描述

还值得一提的是:

  1. 我在Ubuntu上对单声道有相同的精度。

  2. 在玩基准testing的时候,我看到的最大值和一个非常罕见的偏差大约是0.5毫秒(这可能意味着什么,它不是实时系统,但仍值得一提)

  3. 秒表滴答不是TimeSpan滴答。 在Windows 10机器HighResolutionTimer.TickLength是0.23 [ns]。

  4. 基准的 CPU使用率为0.5ms间隔为10%,200ms间隔为0.1%