visual tick interval form event elapsedeventhandler dotnetperls cronometro c# timer

c# - tick - El temporizador más preciso en.NET?



timer event c# (8)

Aquí hay otro enfoque. Preciso dentro de 5-20ms en mi máquina.

public class Run { public Timer timer; public Run() { var nextSecond = MilliUntilNextSecond(); var timerTracker = new TimerTracker() { StartDate = DateTime.Now.AddMilliseconds(nextSecond), Interval = 1000, Number = 0 }; timer = new Timer(TimerCallback, timerTracker, nextSecond, -1); } public class TimerTracker { public DateTime StartDate; public int Interval; public int Number; } void TimerCallback(object state) { var timeTracker = (TimerTracker)state; timeTracker.Number += 1; var targetDate = timeTracker.StartDate.AddMilliseconds(timeTracker.Number * timeTracker.Interval); var milliDouble = Math.Max((targetDate - DateTime.Now).TotalMilliseconds, 0); var milliInt = Convert.ToInt32(milliDouble); timer.Change(milliInt, -1); Console.WriteLine(DateTime.Now.ToString("ss.fff")); } public static int MilliUntilNextSecond() { var time = DateTime.Now.TimeOfDay; var shortTime = new TimeSpan(0, time.Hours, time.Minutes, time.Seconds, 0); var oneSec = new TimeSpan(0, 0, 1); var milliDouble = (shortTime.Add(oneSec) - time).TotalMilliseconds; var milliInt = Convert.ToInt32(milliDouble); return milliInt; } }

Ejecutar el siguiente código (ligeramente pseudo) produce los siguientes resultados. Estoy sorprendido de lo inexacto que es el cronómetro (gana ~ 14ms cada marca).

¿Hay algo más preciso por ahí?

void Main() { var timer = new System.Threading.Timer(TimerCallback, null, 0, 1000); } void TimerCallback(object state) { Debug.WriteLine(DateTime.Now.ToString("ss.ffff")); } Sample Output: ... 11.9109 12.9190 13.9331 14.9491 15.9632 16.9752 17.9893 19.0043 20.0164 21.0305 22.0445 23.0586 24.0726 25.0867 26.1008 27.1148 28.1289 29.1429 30.1570 31.1710 32.1851


Creo que las otras respuestas no abordan por qué hay 14 ms en cada iteración del código del OP; no es debido a un reloj del sistema impreciso (y DateTime.Now no es impreciso, a menos que haya desactivado los servicios NTP o configurado un huso horario incorrecto o ¡algo tonto! Es solo impreciso ).

Temporizador preciso

Incluso con un reloj del sistema impreciso (haciendo uso de DateTime.Now , o tener una célula solar conectada a un ADC para decir qué tan alto está el sol en el cielo, o dividir el tiempo entre las mareas altas, o ...), el código seguir este patrón tendrá un promedio de cero (será perfectamente exacto con exactamente un segundo entre los tics en promedio):

var interval = new TimeSpan(0, 0, 1); var nextTick = DateTime.Now + interval; while (true) { while ( DateTime.Now < nextTick ) { Thread.Sleep( nextTick - DateTime.Now ); } nextTick += interval; // Notice we''re adding onto when the last tick was supposed to be, not when it is now // Insert tick() code here }

(Si está copiando y pegando esto, tenga cuidado con los casos en los que su código tarda más de un interval en ejecutarse. Lo dejaré como un ejercicio para que el lector encuentre las formas fáciles de hacer que salte tantos golpes como se necesita para nextTick aterrizar en el futuro)

Temporizador inexacto

Supongo que la implementación de System.Threading.Timer de Microsoft sigue este tipo de patrón. Este patrón siempre habrá desaparecido incluso con un temporizador de sistema perfectamente preciso y perfectamente preciso (porque lleva tiempo ejecutar incluso la operación de adición):

var interval = new TimeSpan(0, 0, 1); var nextTick = DateTime.Now + interval; while (true) { while ( DateTime.Now < nextTick ) { Thread.Sleep( nextTick - DateTime.Now ); } nextTick = DateTime.Now + interval; // Notice we''re adding onto .Now instead of when the last tick was supposed to be. This is where slew comes from // Insert tick() code here }

Entonces, para las personas que podrían estar interesadas en rodar su propio temporizador, no sigan este segundo patrón.

Medición precisa del tiempo

Como han dicho otros carteles, la clase Stopwatch proporciona una gran precisión para la medición del tiempo, pero no ayuda en absoluto con exactitud si se sigue un patrón incorrecto. Pero, como @Shahar dijo que no es como si fuera a tener un temporizador perfectamente preciso para empezar, entonces debes reconsiderar las cosas si lo que buscas es una precisión perfecta.

Descargo de responsabilidad

Tenga en cuenta que Microsoft no habla mucho sobre los System.Threading.Timer internos de la clase System.Threading.Timer , así que estoy educadamente especulando al respecto, pero si grazna como un pato, entonces es probable que sea un pato. Además, me doy cuenta de que esto tiene varios años, pero sigue siendo una pregunta relevante (y creo que no contestada).

Editar: cambió el enlace a la respuesta de @ Shahar

Editar: Microsoft tiene un código fuente para muchas cosas en línea, incluyendo System.Threading.Timer , para cualquiera que esté interesado en ver cómo Microsoft implementó ese temporizador de sincronización.


El sistema operativo de escritorio (como Windows) no es un sistema operativo en tiempo real. lo que significa que no puede esperar una precisión total y no puede obligar al programador a activar su código en el milisegundo exacto que desee. Especialmente en la aplicación .NET que no es determinista ... por ejemplo, cada vez que el GC puede comenzar a recopilar, una compilación JIT puede ser un poco más lenta o un poco más rápida ....


Hice una clase para eso, y parece estar funcionando bien. Sin inexactitud alguna:

class AccurateTimer { public event EventHandler<EventArgs> Tick; public bool Running { get; private set; } public int Interval { get; private set; } public AccurateTimer(int interval_ = 1000) { Running = false; Interval = interval_; } public void Start() { Running = true; Thread thread = new Thread(Run); thread.Start(); } public void Stop() { Running = false; } private void Run() { DateTime nextTick = DateTime.Now.AddMilliseconds(Interval); while (Running) { if (DateTime.Now > nextTick) { nextTick = nextTick.AddMilliseconds(Interval); OnTick(EventArgs.Empty); } } } protected void OnTick(EventArgs e) { EventHandler<EventArgs> copy = Tick; if (copy != null) { copy(this, e); } } }

Sin embargo, puede que no sea la mejor solución.


No es el temporizador que es inexacto, pero http://blogs.msdn.com/b/ericlippert/archive/2010/04/08/precision-and-accuracy-of-datetime.aspx , que tiene una tolerancia anunciada de 16 ms.

En cambio, usaría la propiedad Environment.Ticks para medir los ciclos de la CPU durante esta prueba.

Editar : Environment.Ticks también se basa en el temporizador del sistema y puede tener los mismos problemas de precisión que DateTime.Now. Aconsejaría elegir el StopWatch lugar, como han mencionado muchos otros contestadores.


Para medir el tiempo exacto, debe utilizar la clase Cronómetro MSDN


También he escrito una clase que tiene una precisión de 1 ms. Tomé el código de Hans Passant del foro
https://social.msdn.microsoft.com/Forums/en-US/6cd5d9e3-e01a-49c4-9976-6c6a2f16ad57/1-millisecond-timer
y lo envolvió en una clase para facilitar su uso en su Formulario. Puede configurar múltiples temporizadores fácilmente si lo desea. En el código de ejemplo a continuación, he usado 2 temporizadores. Lo he probado y funciona bien.

// AccurateTimer.cs using System; using System.Windows.Forms; using System.Runtime.InteropServices; namespace YourProjectsNamespace { class AccurateTimer { private delegate void TimerEventDel(int id, int msg, IntPtr user, int dw1, int dw2); private const int TIME_PERIODIC = 1; private const int EVENT_TYPE = TIME_PERIODIC;// + 0x100; // TIME_KILL_SYNCHRONOUS causes a hang ?! [DllImport("winmm.dll")] private static extern int timeBeginPeriod(int msec); [DllImport("winmm.dll")] private static extern int timeEndPeriod(int msec); [DllImport("winmm.dll")] private static extern int timeSetEvent(int delay, int resolution, TimerEventDel handler, IntPtr user, int eventType); [DllImport("winmm.dll")] private static extern int timeKillEvent(int id); Action mAction; Form mForm; private int mTimerId; private TimerEventDel mHandler; // NOTE: declare at class scope so garbage collector doesn''t release it!!! public AccurateTimer(Form form,Action action,int delay) { mAction = action; mForm = form; timeBeginPeriod(1); mHandler = new TimerEventDel(TimerCallback); mTimerId = timeSetEvent(delay, 0, mHandler, IntPtr.Zero, EVENT_TYPE); } public void Stop() { int err = timeKillEvent(mTimerId); timeEndPeriod(1); System.Threading.Thread.Sleep(100);// Ensure callbacks are drained } private void TimerCallback(int id, int msg, IntPtr user, int dw1, int dw2) { if (mTimerId != 0) mForm.BeginInvoke(mAction); } } } // FormMain.cs using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace YourProjectsNamespace { public partial class FormMain : Form { AccurateTimer mTimer1,mTimer2; public FormMain() { InitializeComponent(); } private void FormMain_Load(object sender, EventArgs e) { int delay = 10; // In milliseconds. 10 = 1/100th second. mTimer1 = new AccurateTimer(this, new Action(TimerTick1),delay); delay = 100; // 100 = 1/10th second. mTimer2 = new AccurateTimer(this, new Action(TimerTick2), delay); } private void FormMain_FormClosing(object sender, FormClosingEventArgs e) { mTimer1.Stop(); mTimer2.Stop(); } private void TimerTick1() { // Put your first timer code here! } private void TimerTick2() { // Put your second timer code here! } } }