Pregunta

Estoy escribiendo un servicio de Windows que se ejecuta una actividad de longitud variable a intervalos (una exploración de la base de datos y actualizar). Necesito esta tarea a ejecutar con frecuencia, pero el código de manejar no es seguro para ejecutar varias veces al mismo tiempo.

¿Cómo puedo mayoría simplemente establecer un temporizador para ejecutar la tarea cada 30 segundos mientras que nunca se superponen las ejecuciones? (Estoy asumiendo System.Threading.Timer es el contador de tiempo correcto para este trabajo, pero podría estar equivocado).

¿Fue útil?

Solución

Usted podría hacerlo con un temporizador, pero que tendría que tener algún tipo de bloqueo en su exploración de la base de datos y actualizar. A lock sencilla para sincronizar puede ser suficiente para evitar que múltiples carreras que se produzcan.

Dicho esto, podría ser mejor comenzar un temporizador después de que haya operación se ha completado, y sólo lo utilizan una vez, y luego se detiene. Reiniciarlo después de su próxima operación. Esto le daría 30 segundos (o N segundos) entre los eventos, sin posibilidad de solapamientos, y ningún 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);

El trabajo de inmediato ..... Finalizar ... espere 5 segundos .... trabajar inmediatamente ..... Finalizar ... Espere 5 segundos ....

Otros consejos

que haría uso de Monitor.TryEnter en el 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
}

Yo prefiero System.Threading.Timer para este tipo de cosas, porque no tengo que pasar por el evento mecanismo de manejo:

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 de la cerradura haciendo que el temporizador de un one-shot y volver a ponerlo en marcha 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);
}

en lugar de bloqueo (que podría causar todos los escaneados programados para esperar y finalmente apile). Se podría empezar la exploración / actualización de un hilo y luego simplemente hacer una comprobación para ver si el hilo está todavía vivo.

Thread updateDBThread = new Thread(MyUpdateMethod);

...

private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
    if(!updateDBThread.IsAlive)
        updateDBThread.Start();
}

Se puede utilizar 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...
        }
   }
}

A ligeramente solución "inteligente" es como sigue en pseudo-có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 se puede apagar el hilo, simplemente mediante la señalización del evento.


Editar

debo añadir, que este código hace la suposición de que sólo tiene uno de estos MyWorkerThreads () en funcionamiento, de lo contrario se ejecutará simultáneamente.

He utilizado un mutex cuando me he querido 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();
                }
            }
        }

    }

En este momento estoy tan tarde ... Usted tendrá que proporcionar el nombre de exclusión mutua como parte de la GetMutexName llamada al método ().

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top