System.Threading.Timer en C # parece que no funciona. Corre muy rápido cada 3 segundos

Tengo un objeto de temporizador. Quiero que se ejecute cada minuto. Específicamente, debe ejecutar un método OnCallBack y se vuelve inactivo mientras se ejecuta un método OnCallBack . Una vez que finaliza un método OnCallBack , este (un OnCallBack ) reinicia un temporizador.

Esto es lo que tengo ahora:

 private static Timer timer; private static void Main() { timer = new Timer(_ => OnCallBack(), null, 0, 1000 * 10); //every 10 seconds Console.ReadLine(); } private static void OnCallBack() { timer.Change(Timeout.Infinite, Timeout.Infinite); //stops the timer Thread.Sleep(3000); //doing some long operation timer.Change(0, 1000 * 10); //restarts the timer } 

Sin embargo, parece no estar funcionando. Corre muy rápido cada 3 segundos. Incluso si aumenta un período (1000 * 10). Parece que hace la vista gorda a 1000 * 10

¿Qué hice mal?

Este no es el uso correcto del System.Threading.Timer. Cuando crea una instancia del temporizador, casi siempre debe hacer lo siguiente:

 _timer = new Timer( Callback, null, TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite ); 

Esto le indicará al temporizador que marque solo una vez cuando haya transcurrido el intervalo. Luego, en su función de callback, cambia el temporizador una vez que el trabajo se haya completado, no antes. Ejemplo:

 private void Callback( Object state ) { // Long running operation _timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite ); } 

Por lo tanto, no hay necesidad de mecanismos de locking porque no hay concurrencia. El temporizador disparará la próxima callback después de que haya transcurrido el siguiente intervalo + el tiempo de la operación de larga ejecución.

Si necesita ejecutar su temporizador a exactamente N milisegundos, entonces le sugiero que mida el tiempo de la operación de larga ejecución usando Cronómetro y luego llame al método de Cambio de manera apropiada:

 private void Callback( Object state ) { Stopwatch watch = new Stopwatch(); watch.Start(); // Long running operation _timer.Change( Math.Max( 0, TIME_INTERVAL_IN_MILLISECONDS - watch.ElapsedMilliseconds ), Timeout.Infinite ); } 

Recomiendo encarecidamente a cualquier persona que haga .NET y que esté utilizando el CLR que no haya leído el libro de Jeffrey Richter, CLR mediante C # , para leerlo lo antes posible. Los temporizadores y grupos de hilos se explican con gran detalle allí.

No es necesario detener el temporizador, ver buena solución de esta publicación :

“Puede dejar que el temporizador continúe activando el método de callback, pero ajuste su código no reentrante en Monitor.TryEnter / Exit. No es necesario detener / reiniciar el temporizador en ese caso; las llamadas superpuestas no obtendrán el locking y regresarán de inmediato”.

 private void CreatorLoop(object state) { if (Monitor.TryEnter(lockObject)) { try { // Work here } finally { Monitor.Exit(lockObject); } } } 

¿Está utilizando System.Threading.Timer obligatorio?

De lo contrario, System.Timers.Timer tiene prácticos métodos Start () y Stop () (y una propiedad AutoReset que puede establecer en false, para que Stop () no sea necesario y simplemente llame a Start () después de ejecutar).

Solo lo haría:

 private static Timer timer; private static void Main() { timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds Console.ReadLine(); } private static void OnCallBack() { timer.Dispose(); Thread.Sleep(3000); //doing some long operation timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds } 

E ignore el parámetro de período, ya que está intentando controlar la periodicidad usted mismo.


Su código original se está ejecutando lo más rápido posible, ya que sigue especificando 0 para el parámetro dueTime . De Timer.Change :

Si dueTime es cero (0), el método de callback se invoca inmediatamente.

  var span = TimeSpan.FromMinutes(2); var t = Task.Factory.StartNew(async delegate / () => { this.SomeAsync(); await Task.Delay(span, source.Token); }, source.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); source.Cancel(true/or not); // or use ThreadPool(whit defaul options thread) like this Task.Start(()=>{...}), source.Token) 

si te gusta usar algún hilo de bucle dentro …

 public async void RunForestRun(CancellationToken token) { var t = await Task.Factory.StartNew(async delegate { while (true) { await Task.Delay(TimeSpan.FromSeconds(1), token) .ContinueWith(task => { Console.WriteLine("End delay"); }); this.PrintConsole(1); } }, token) // drop thread options to default values; } // And somewhere there source.Cancel(); //or token.ThrowIfCancellationRequested(); // try/ catch block requred.