Frage

Ich schreibe einen Windows-Dienst, der eine variable Länge Aktivität in Intervallen ausgeführt wird (eine Datenbank Scan-und Update). Ich brauche diese Aufgabe häufig zu laufen, aber der Code zu handhaben ist nicht sicher mehrmals gleichzeitig ausgeführt werden.

Wie kann ich am einfachsten einen Timer einstellen, bis die Aufgabe alle 30 Sekunden zu laufen, während nie Hinrichtung überlappende? (Ich gehe davon aus System.Threading.Timer die richtigen Zeitgeber für diesen Job ist, aber könnte verwechselt werden).

War es hilfreich?

Lösung

Sie können es mit einem Timer tun, aber Sie würden eine Form von Sperren auf Ihrer Datenbank Scan-und Update haben müssen. Eine einfache lock zu synchronisieren kann genug sein, mehrere Durchläufe zu verhindern.

Dass gesagt wird, könnte es besser sein, einen Timer zu starten, nachdem Sie sind Vorgang abgeschlossen ist, und es nur ein einziges Mal verwendet wird, dann ist es zu stoppen. Starten Sie es nach Ihrem nächsten Operation. Dies würden Sie 30 Sekunden (oder N Sekunden) zwischen den Ereignissen, ohne die Möglichkeit von Überlappungen und keine Verriegelung.

Beispiel:

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);

Arbeit sofort ..... Fertig ... warten 5 Sekunden .... Arbeiten sofort ..... Fertig ... warten Sie 5 Sekunden ....

Andere Tipps

I Monitor.TryEnter in Ihrem verstrichene Code verwenden würde:

if (Monitor.TryEnter(lockobj))
{
  try
  {
    // we got the lock, do your work
  }
  finally
  {
     Monitor.Exit(lockobj);
  }
}
else
{
  // another elapsed has the lock
}

Ich ziehe System.Threading.Timer für Dinge wie diese, weil ich zu der Veranstaltung nicht durch Mechanismus Handhabung:

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.
    }
}

Sie können die Notwendigkeit für die Sperre beseitigen, indem Sie den Timer macht ein One-Shot und Neustarten es nach jedem Update:

// 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);
}

anstelle von Sperren (die alle Ihre timed Scans dazu führen könnten, zu warten und schließlich stapeln). Sie können die Scan / Update in einem Thread starten und dann einfach eine Überprüfung, ob der Faden noch am Leben ist.

Thread updateDBThread = new Thread(MyUpdateMethod);

...

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

Sie könnten die Autoreset wie folgt verwenden:

// 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...
        }
   }
}

Ein etwas "intelligente" Lösung ist wie folgt in Pseudo-Code:

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;
        }
     }
   }
 }

Jede dieser hat den Vorteil, dass Sie Abschaltung der Faden, nur durch das Ereignis signalisiert wird.


Bearbeiten

Ich sollte hinzufügen, dass dieser Code die Annahme macht, dass Sie nur eine dieser MyWorkerThreads haben () laufen, sonst würden sie gleichzeitig ausgeführt werden.

Ich habe ein Mutex verwendet, wenn ich einzelne Ausführung gewünscht haben:

    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();
                }
            }
        }

    }

Leider habe ich so spät bin ... Sie müssen die Mutex Namen als Teil des GetMutexName () -Methode zur Verfügung zu stellen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top