c# - ejemplo - system threading timer example
System.Threading.Timer en C#parece que no funciona. Corre muy rĂ¡pido cada 3 segundos (5)
¿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).
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 devolución de llamada, 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 bloqueo porque no hay concurrencia. El temporizador disparará la próxima devolución de llamada 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 devolución de llamada, 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 bloqueo y regresarán de inmediato".
private void CreatorLoop(object state)
{
if (Monitor.TryEnter(lockObject))
{
try
{
// Work here
}
finally
{
Monitor.Exit(lockObject);
}
}
}
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 devolución de llamada 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.