Minuterie haute résolution en C #

Existe-t-il un minuteur haute résolution qui déclenche un événement chaque fois que celui-ci s’écoule, comme dans la classe System.Timer ? J’ai besoin d’un minuteur haute résolution pour Elapse chaque ms.

Je n’arrête pas de lire des articles expliquant que le chronomètre peut mesurer des résolutions élevées, mais je ne veux pas mesurer le temps, je veux créer un intervalle de 1 ms.

Y a-t-il quelque chose dans .NET ou vais-je écrire mon propre chronomètre haute résolution?

À ma connaissance, le framework .NET ne contient aucun élément. Windows dispose d’un mécanisme pour les événements du minuteur haute résolution via l’ API Multimedia Timer . Ci-dessous, un exemple rapide que j’ai préparé, qui semble faire l’affaire. Il semble également y avoir un bon exemple ici .

Je noterai que cette API modifie les parameters de l’ensemble du système susceptibles de dégrader les performances du système. Méfiez-vous donc. À des fins de test, je vous conseillerais de noter la fréquence d’activation du minuteur pour vérifier que le minutage est similaire à celui que vous essayez de simuler. Comme Windows n’est pas un système d’exploitation en temps réel, la charge de votre système peut retarder le temporisateur MM, ce qui entraîne des intervalles de 100 ms contenant 100 événements en succession rapide, plutôt que 100 événements espacés de 1 ms. Quelques lectures supplémentaires sur les timers MM.

 class Program { static void Main(ssortingng[] 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); } 

Vous pouvez avoir un minuteur haute résolution basé sur Chronomètre qui peut fournir une résolution bien supérieure à 1 ms sur les systèmes actuels.

Voici mon autre réponse sur Stackoverflow https://stackoverflow.com/a/45097518/548894

Une implémentation https://gist.github.com/DraTeots/436019368d32007284f8a12f1ba0f545

  1. Cela fonctionne sur toutes les plateformes et est de haute précision partout où StopWatch.IsHighPrecision == true

  2. Il est garanti que son événement Elapsed se chevauche pas (ce qui peut être important à connaître, car les changements d’état à l’intérieur du gestionnaire d’événements peuvent ne pas être protégés contre les access multithreads).

Voici comment l’utiliser:

 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 

Voici un sharepoint repère (et un exemple concret):
https://gist.github.com/DraTeots/5f454968ae84122b526651ad2d6ef2a3

Les résultats de la configuration du minuteur pour 0,5 ms sur Windows 10: entrez la description de l'image ici

Il convient également de mentionner que:

  1. J’ai eu la même précision sur mono sur Ubuntu.

  2. En jouant avec le repère, l’écart maximal et très rare que j’ai vu était d’environ 0,5 ms (ce qui ne signifie probablement rien, ce ne sont pas des systèmes en temps réel, mais il convient de mentionner)

  3. Les ticks du chronomètre ne sont pas des ticks de TimeSpan. Sur cette machine Windows 10, HighResolutionTimer.TickLength est égal à 0.23 [ns].

Essayez de créer un nouveau System.Threading.Thread et d’utiliser System.Threading.Thread.Sleep .

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