despues - actualizar datagridview c# en tiempo de ejecucion
Sincronización de un temporizador para evitar la superposición. (6)
Estoy escribiendo un servicio de Windows que ejecuta una actividad de longitud variable a intervalos (un análisis y actualización de la base de datos). Necesito esta tarea para ejecutar con frecuencia, pero el código para manejar no es seguro para ejecutarse varias veces al mismo tiempo.
¿Cómo puedo simplemente configurar un temporizador para ejecutar la tarea cada 30 segundos mientras nunca se superponen las ejecuciones? (Estoy asumiendo que System.Threading.Timer
es el temporizador correcto para este trabajo, pero podría estar equivocado).
He usado un mutex cuando he querido una sola ejecución:
private void OnMsgTimer(object sender, ElapsedEventArgs args)
{
// mutex creates a single instance in this application
bool wasMutexCreatedNew = false;
using(Mutex onlyOne = new Mutex(true, GetMutexName(), out wasMutexCreatedNew))
{
if (wasMutexCreatedNew)
{
try
{
//<your code here>
}
finally
{
onlyOne.ReleaseMutex();
}
}
}
}
Lo siento, llego tan tarde ... Deberá proporcionar el nombre de exclusión mutua como parte de la llamada al método GetMutexName ().
Podría hacerlo con un temporizador, pero tendría que tener algún tipo de bloqueo en el escaneo y actualización de su base de datos. Un simple lock
para sincronizar puede ser suficiente para evitar que ocurran múltiples ejecuciones.
Dicho esto, podría ser mejor iniciar un temporizador DESPUÉS de que finalice la operación, y solo úselo una vez y luego deténgalo. Reinícielo después de su próxima operación. Esto le daría 30 segundos (o N segundos) entre eventos, sin posibilidad de superposiciones y sin bloqueo.
Ejemplo:
System.Threading.Timer timer = null;
timer = new System.Threading.Timer((g) =>
{
Console.WriteLine(1); //do whatever
timer.Change(5000, Timeout.Infinite);
}, null, 0, Timeout.Infinite);
Trabaja de inmediato ... Finaliza ... espera 5 segundos ... Trabaja de inmediato ... Finaliza ... espera 5 segundos ...
Podría usar el AutoResetEvent de la siguiente manera:
// Somewhere else in the code
using System;
using System.Threading;
// In the class or whever appropriate
static AutoResetEvent autoEvent = new AutoResetEvent(false);
void MyWorkerThread()
{
while(1)
{
// Wait for work method to signal.
if(autoEvent.WaitOne(30000, false))
{
// Signalled time to quit
return;
}
else
{
// grab a lock
// do the work
// Whatever...
}
}
}
Una solución ligeramente "más inteligente" es la siguiente en el pseudocódigo:
using System;
using System.Diagnostics;
using System.Threading;
// In the class or whever appropriate
static AutoResetEvent autoEvent = new AutoResetEvent(false);
void MyWorkerThread()
{
Stopwatch stopWatch = new Stopwatch();
TimeSpan Second30 = new TimeSpan(0,0,30);
TimeSpan SecondsZero = new TimeSpan(0);
TimeSpan waitTime = Second30 - SecondsZero;
TimeSpan interval;
while(1)
{
// Wait for work method to signal.
if(autoEvent.WaitOne(waitTime, false))
{
// Signalled time to quit
return;
}
else
{
stopWatch.Start();
// grab a lock
// do the work
// Whatever...
stopwatch.stop();
interval = stopwatch.Elapsed;
if (interval < Seconds30)
{
waitTime = Seconds30 - interval;
}
else
{
waitTime = SecondsZero;
}
}
}
}
Cualquiera de estos tiene la ventaja de que puede cerrar el hilo, solo con señalar el evento.
Editar
Debo agregar, que este código supone que solo tienes uno de estos MyWorkerThreads () en ejecución, de lo contrario, se ejecutarán de forma concurrente.
Prefiero System.Threading.Timer
para cosas como esta, porque no tengo que pasar por el mecanismo de manejo de eventos:
Timer UpdateTimer = new Timer(UpdateCallback, null, 30000, 30000);
object updateLock = new object();
void UpdateCallback(object state)
{
if (Monitor.TryEnter(updateLock))
{
try
{
// do stuff here
}
finally
{
Monitor.Exit(updateLock);
}
}
else
{
// previous timer tick took too long.
// so do nothing this time through.
}
}
Puede eliminar la necesidad del bloqueo haciendo que el temporizador sea un disparo único y reiniciarlo después de cada actualización:
// Initialize timer as a one-shot
Timer UpdateTimer = new Timer(UpdateCallback, null, 30000, Timeout.Infinite);
void UpdateCallback(object state)
{
// do stuff here
// re-enable the timer
UpdateTimer.Change(30000, Timeout.Infinite);
}
Yo usaría Monitor.TryEnter en su código transcurrido:
if (Monitor.TryEnter(lockobj))
{
try
{
// we got the lock, do your work
}
finally
{
Monitor.Exit(lockobj);
}
}
else
{
// another elapsed has the lock
}
en lugar de bloquear (lo que podría hacer que todas las exploraciones programadas esperen y finalmente se acumulen). Podría iniciar la exploración / actualización en un hilo y luego simplemente hacer una comprobación para ver si el hilo aún está vivo.
Thread updateDBThread = new Thread(MyUpdateMethod);
...
private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
if(!updateDBThread.IsAlive)
updateDBThread.Start();
}