C#中的高分辨率计时器

是否存在高分辨率计时器,每次计时器过去时都会引发事件,就像System.Timer类一样? 我需要一个高分辨率的计时器来消除每一个ms。

我一直在讨论秒表可以测量高分辨率的post,但我不想测量时间,我想创建一个1毫秒的间隔。

在.NET中有什么东西,还是我要编写自己的高分辨率计时器?

我所知道的.NET框架中没有任何内置function。 Windows具有通过Multimedia Timer API实现高分辨率计时器事件的机制。 下面是一个快速的例子,我掀起了似乎做的工作。 这里似乎也有一个很好的例子。

我会注意到这个API会改变系统范围的设置,这会降低系统性能,所以买家要小心。 出于测试目的,我建议跟踪定时器触发的频率,以validation定时与您尝试模拟的设备类似。 由于Windows不是实时操作系统,因此系统上的负载可能会导致MM计时器延迟,导致100 ms的间隙快速连续包含100个事件,而不是间隔1 ms的100个事件。 关于MM计时器的一些额外阅读 。

 class Program { static void Main(string[] args) { TestThreadingTimer(); TestMultimediaTimer(); } private static void TestMultimediaTimer() { Stopwatch s = new Stopwatch(); using (var timer = new MultimediaTimer() { Interval = 1 }) { timer.Elapsed += (o, e) => Console.WriteLine(s.ElapsedMilliseconds); s.Start(); timer.Start(); Console.ReadKey(); timer.Stop(); } } private static void TestThreadingTimer() { Stopwatch s = new Stopwatch(); using (var timer = new Timer(o => Console.WriteLine(s.ElapsedMilliseconds), null, 0, 1)) { s.Start(); Console.ReadKey(); } } } public class MultimediaTimer : IDisposable { private bool disposed = false; private int interval, resolution; private UInt32 timerId; // Hold the timer callback to prevent garbage collection. private readonly MultimediaTimerCallback Callback; public MultimediaTimer() { Callback = new MultimediaTimerCallback(TimerCallbackMethod); Resolution = 5; Interval = 10; } ~MultimediaTimer() { Dispose(false); } public int Interval { get { return interval; } set { CheckDisposed(); if (value < 0) throw new ArgumentOutOfRangeException("value"); interval = value; if (Resolution > Interval) Resolution = value; } } // Note minimum resolution is 0, meaning highest possible resolution. public int Resolution { get { return resolution; } set { CheckDisposed(); if (value < 0) throw new ArgumentOutOfRangeException("value"); resolution = value; } } public bool IsRunning { get { return timerId != 0; } } public void Start() { CheckDisposed(); if (IsRunning) throw new InvalidOperationException("Timer is already running"); // Event type = 0, one off event // Event type = 1, periodic event UInt32 userCtx = 0; timerId = NativeMethods.TimeSetEvent((uint)Interval, (uint)Resolution, Callback, ref userCtx, 1); if (timerId == 0) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error); } } public void Stop() { CheckDisposed(); if (!IsRunning) throw new InvalidOperationException("Timer has not been started"); StopInternal(); } private void StopInternal() { NativeMethods.TimeKillEvent(timerId); timerId = 0; } public event EventHandler Elapsed; public void Dispose() { Dispose(true); } private void TimerCallbackMethod(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2) { var handler = Elapsed; if (handler != null) { handler(this, EventArgs.Empty); } } private void CheckDisposed() { if (disposed) throw new ObjectDisposedException("MultimediaTimer"); } private void Dispose(bool disposing) { if (disposed) return; disposed = true; if (IsRunning) { StopInternal(); } if (disposing) { Elapsed = null; GC.SuppressFinalize(this); } } } internal delegate void MultimediaTimerCallback(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2); internal static class NativeMethods { [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeSetEvent")] internal static extern UInt32 TimeSetEvent(UInt32 msDelay, UInt32 msResolution, MultimediaTimerCallback callback, ref UInt32 userCtx, UInt32 eventType); [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeKillEvent")] internal static extern void TimeKillEvent(UInt32 uTimerId); } 

你可以拥有一个基于秒表的高分辨率计时器,它可以提供比现今系统更好的1ms分辨率。

这是我在Stackoverflow上的另一个答案https://stackoverflow.com/a/45097518/548894

实现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 ms的结果: 在此处输入图像描述

值得一提的是:

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

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

  3. 秒表刻度不是TimeSpan刻度。 在那台Windows 10机器上HighResolutionTimer.TickLength是0.23 [ns]。

尝试创建新的System.Threading.Thread并使用System.Threading.Thread.Sleep

 var thrd = new Syatem.Threading.Thread(() => { while (true) { // do something System.Threading.Thread.Sleep(1); // wait 1 ms } }); thrd.Start();