2012-03-24 12 views
2

Arbeiten an einem Windows-Dienst, der Anfrage in jedem vordefinierten Zeitintervall verarbeiten muss. Thread.Sleep funktioniert einwandfrei, aber das Problem tritt auf, wenn der Dienst aufgerufen wird, um ihn zu stoppen, der Dienst wird eingefroren, wenn sich der Thread im Schlafmodus befindet. Ich habe über den alternativen Ansatz wie Timer gelesen, aber das Problem ist, dass nach diesem definierten Intervall neuen Thread gestartet wird. Gibt es eine bessere Möglichkeit, das gleiche Ergebnis zu erzielen und nicht zur Ausgabe zu führen.Alternative zu Thread.Sleep

+2

Starten Sie einen neuen Thread und legen Sie diesen in den Ruhezustand, anstatt den Dienst möglicherweise in den Ruhezustand zu versetzen. Es scheint ein Designproblem zu sein. –

+0

Warum gehst du nicht für einen Timer, der in regelmäßigen Abständen aufwacht? – GETah

+3

Warum verwenden Sie kein Ereignis, um zu signalisieren, dass eine neue Anfrage bereit ist und auf dieses Ereignis wartet. Schlafen und Timer sind zweitklassige Lösungen. –

Antwort

2

Sie können stattdessen WaitHandle.WaitOne verwenden. Sie können darauf warten, dass das schließende Ereignis ausgelöst wird oder das Zeitlimit, das Sie in im vordefinierten Zeitintervall angeben.

static AutoResetEvent seviceStopRequested = new AutoResetEvent(false); 
.... 
((AutoResetEvent)stateInfo).WaitOne([timeout], false) 

Dann, wenn Service-Stopp aufgerufen wird, können Sie nur das Ereignis auslösen einen Timer

seviceStopRequested .Set(); 
1

Verwenden Sie Befehle/Aufgaben einschließlich der Aufgabe, für das Herunterfahren zu einer Blockierung Warteschlange hinzuzufügen. Stellen Sie Ihren Dienst-Thread so ein, dass er auf Aufgaben in der blockierenden Warteschlange wartet, und führen Sie sie aus, wenn sie verfügbar sind. Der Timer-Thread fügt die Aufgaben regelmäßig zur Warteschlange hinzu.

3

Was Sie suchen, ist die Fähigkeit, auf die Benachrichtigung von zwei verschiedenen Ereignissen zu reagieren - (1) wenn der Timer abläuft und (2) wenn der Dienst gestoppt wird. @Anurag Ranhjan ist auf der richtigen Spur mit WaitHandle, aber Sie haben zwei Ereignisse, nicht eins. Gehen Sie folgendermaßen vor, um das ordnungsgemäß zu behandeln.

Definieren Sie zuerst die zwei Ereignisse, die Sie interessieren, indem Sie ManualResetEvent verwenden. Sie können AutoResetEvent verwenden, wenn Sie bevorzugen; Ich möchte nur die Ereignisse manuell zurücksetzen.

using System.Threading; 
ManualResetEvent shutdownEvent = new ManualResetEvent(); 
ManualResetEvent elapsedEvent = new ManualResetEvent(); 

Sie müssen diese Ereignisse auslösen, wenn sie auftreten. Für die shutdownEvent ist es einfach. Stellen Sie das Ereignis im OnStop Rückruf Ihres Windows-Dienstes einfach ein.

protected override void OnStop 
{ 
    shutdownEvent.Set(); 
} 

Für die elapsedEvent, könnten Sie dies ein paar verschiedene Arten tun. Sie könnten einen Hintergrund-Thread erstellen, d. H. Den ThreadPool, der Thread.Sleep verwendet. Wenn der Thread aufwacht, stellen Sie elapsedEvent ein und gehen Sie wieder in den Ruhezustand. Da es sich um einen Hintergrundthread handelt, hängt es Ihren Dienst nicht, wenn er heruntergefahren wird. Die Alternative, wie Sie bereits vorgeschlagen haben, ist die Verwendung eines Timers. So mache ich es.

using System.Timers; 
Timer timer = new Timer(); 
timer.Interval = 5000; // in milliseconds 
timer.Elapsed += delegate { elapsedEvent.Set(); }; 
timer.AutoReset = false; // again, I prefer manual control 
timer.Start(); 

Nun, da Sie Ereignisse habe richtig eingestellt ist, setzen Sie sie in einem WaitHandle Array.

WaitHandle[] handles = new WaitHandle[] 
{ 
    shutdownEvent, 
    elapsedEvent 
}; 

Anstelle der WaitHandle.WaitOne Methode verwenden, um die WaitHandle.WaitAny Methode innerhalb einer while-Schleife, wie diese.

while (!shutdownEvent.WaitOne()) 
{ 
    switch (WaitHandle.WaitAny(handles)) 
    { 
     case 0: // The shutdownEvent was triggered! 
      break; 
     case 1: // The elapsedEvent was triggered! 
      Process();    // do your processing here 
      elapsedEvent.Reset(); // reset the event manually 
      timer.Start();   // restart the timer manually 
      break; 
     default: 
      throw new Exception("unexpected switch case"); 
    } 
} 

Ich habe dieses Beispiel aus dem Produktionscode in meinem Projekt zusammengefasst. Ich weiß, dass dieser Mechanismus funktioniert, aber ich habe vielleicht etwas in der Beschreibung verpasst. Lass es mich wissen, wenn du irgendwelche Fragen hast.

+2

Bitte empfehle 'System.Timers.Timer' nicht. Es enthält nicht abgefangene Ausnahmen, wodurch alles scheinbar funktioniert. Wechseln Sie zu einem Beispiel, das 'System.Threading.Timer' stattdessen verwendet. – jgauffin

0

Für was es wert ist die meisten Blockierungsaufrufe in der .NET BCL reagieren auf Thread.Interrupt. Das heißt, sie warten nicht auf die gesamte Zeit, die beim Aufruf angegeben wurde, sondern kehren stattdessen sofort zurück.Ich würde jedoch vermeiden, diese Methode zu verwenden und stattdessen eine einzelne ManualResetEvent verwenden, um sowohl das Leerlaufwarte- als auch das Abschaltsignal durchzuführen. Es würde so aussehen.

public class MyServer : ServiceBase 
{ 
    private ManualResetEvent shutdown = new ManualResetEvent(false); 

    protected override void OnStart(string[] args) 
    { 
    new Thread(
    () => 
     { 
     while (!shutdown.WaitOne(YourInterval)) 
     { 
      // Do work here. 
     } 
     }).Start(); 
    } 

    protected override void OnStop() 
    { 
    shutdown.Set(); 
    } 
} 
2

ich in der Regel das folgende Muster verwenden:

public class MyJob 
{ 
    System.Threading.Timer _timer; 
    bool _isStopped; 

    public void MyJob() 
    { 
     _timer = new Timer(OnWork, null, TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(-1)); 
    } 

    private void OnWork(object state) 
    { 
     //[.. do the actual work here ..] 

     if (!_isStopped) 
      _timer.Change(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(-1)); 
    } 

    public void Stop() 
    { 
     _isStopped = true; 
     _timer.Change(TimeSpan.FromSeconds(-1), TimeSpan.FromSeconds(-1)); 
    } 

    public void Start() 
    { 
     _isStopped = false; 
     _timer.Change(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(-1)); 
    } 
} 

Kernpunkte:

  • nur das Anfangsintervall unter Verwendung gibt Ihnen die volle Kontrolle über, wenn der Timer wieder gestartet wird (dh die Arbeit Zeit wird nicht im Timerintervall gezählt)
  • Wenn Sie den Timer auf -1 Sekunden setzen, wird die Zeit bis zur nächsten Änderung unterbrochen

Es sollte daher mit allen Ihren Anforderungen arbeiten.