2012-05-17 5 views
5

Ich habe einen Multi-Thread-Windows-Dienst in .NET 3.5, und ich habe einige Probleme, den Dienst ordnungsgemäß zu stoppen, wenn mehr als ein Thread erstellt wird.Stoppen eines Multi-Thread-Windows-Dienstes

Dieser Dienst verwendet, um nur einen Thread zu erstellen, um die ganze Arbeit zu erledigen, und ich änderte es einfach zu Multithreading. Es funktioniert perfekt, aber wenn der Dienst beendet wird, wenn mehr als ein Thread ausgeführt wird, hängt es den Dienst, bis alle Threads abgeschlossen sind.

Wenn der Dienst gestartet wird, schaffe ich einen Hintergrund-Thread den Hauptprozess zu handhaben:

protected override void OnStart(string[] args) 
    { 
     try 
     { 
      //Global variable that is checked by threads to learn if service was stopped 
      DeliveryConstant.StopService = false; 
      bool SetMaxThreadsResult = ThreadPool.SetMaxThreads(10, 10); 

      ThreadStart st = new ThreadStart(StartThreadPool); 
      workerThread = new Thread(st); 
      workerThread.IsBackground = true; 
      serviceStarted = true; 
      workerThread.Start(); 
     } 
     catch (Exception ex) 
     { 
      //Log something; 
     } 

Hier ist die StartThreadPool Methode:

//Tried with and without this attribute with no success... 
    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)] 
    public void StartThreadPool() 
    { 
     while (serviceStarted) 
     { 
      ProcessInfo input = new ProcessInfo(); 

      try 
      { 
       int? NumPendingRequests = GetItems(50, (Guid?)input.ProcessID); 

       if (NumPendingRequests > 0) 
       { 
        input.ProcessType = 1; 
        input.ProcessID = Guid.NewGuid(); 
        ThreadPool.QueueUserWorkItem(new WaitCallback(new DispatchManager().ProcessRequestList), input); 
       } 
      } 
      catch (Exception ex) 
      { 
       //Some Logging here 
      } 
     } 

     DeliveryConstant.StopService = true; 
    } 

I eine statische Variable in einer getrennten Klasse erstellt um die Threads zu benachrichtigen, dass der Dienst beendet wurde. Wenn der Wert für diese Variable wahr ist, sollten alle Fäden in die Hauptschleife stoppen (a für jede Schleife):

 public static bool StopService; 

schließlich die OnStop Methode:

protected override void OnStop() 
    { 
     DeliveryConstant.StopService = true; 

     //flag to tell the worker process to stop 
     serviceStarted = false; 

     workerThread.Join(TimeSpan.FromSeconds(30)); 
    } 

Im ProcessRequestList Verfahren, bei dem Am Ende jeder foreach überprüfe ich den Wert der StopService-Variable. Wenn das stimmt, breche ich die Schleife.

Hier ist das Problem: Die Threads werden in Blöcken von 50 Elementen erstellt. Wenn ich 50 Elemente oder weniger in der Datenbank habe, wird nur ein Thread erstellt, und alles funktioniert wunderbar. Wenn ich mehr als 50 Elemente habe, werden mehrere Threads erstellt, und wenn ich versuche, den Dienst zu stoppen, stoppt es nicht, bis alle Hintergrundthreads abgeschlossen sind.

Aus den Protokollen kann ich sehen, dass die Methode OnStop nur ausgeführt wird, nachdem alle Threads abgeschlossen sind.

Jeder Hinweis, was könnte geändert werden, um das zu beheben?

+0

Versuchen Sie, einen Schlaf (50) in die While-Schleife zu setzen. –

+0

Es scheint mir, dass Sie den wichtigsten Teil des Codes nicht enthalten haben. Das ist die Implementierung der ProcessRequestList-Methode. –

+0

Können Sie Code anzeigen, in dem StopService abgefragt wird? Schauen Sie sich die Verwendung von CancellationToken über statische var an, wie es für diese Art von Sache gemacht wird. – hatchet

Antwort

9

Diese blog answer besagt, dass OnStop nicht aufgerufen wird, bis alle ThreadPool-Aufgaben abgeschlossen sind, was für mich neu ist, aber Ihr Problem erklären würde.

Ich habe viele Multithread-Windows-Dienste installiert, aber ich bevorzuge es, eigene Hintergrundthreads zu erstellen, anstatt den ThreadPool zu verwenden, da diese lange laufende Threads sind. Ich instanziiere Arbeiterklassen und starte ihre DoWork() -Methode für den Thread. Ich bevorzuge auch Callbacks für die startende Klasse, um nach einem Stoppsignal zu suchen und den Status zu übergeben, anstatt nur mit einer globalen Variablen zu testen.

+0

Sie genau das Problem gelöst. Danke vielmals! Das ist so ziemlich das, was ich gesagt habe, aber ich wusste nicht, dass es ein erwartetes Verhalten war. Wenn ich das weiß, prüfe ich jetzt den Dienststatus in den Arbeits-Threads und es funktioniert wie ein Zauber. Nicht sicher (noch) wie teuer es für die Leistung sein wird. Vielen Dank! – Roberto

+0

Roberto, würde es Ihnen etwas ausmachen, Ihre Lösung in Ihrer Frage zu posten, oder ist das die boolesche Variable StopService? – PAULDAWG

3

Ihnen fehlen Speicherbarrieren um Zugriffe auf StopService, die ein Problem sein können, wenn Sie mehrere CPUs haben. Sperren Sie besser jedes Referenzobjekt für ALLE Zugriffe auf die gemeinsame Variable. Zum Beispiel:

object @lock; 

... 

lock (@lock) 
{ 
    StopService = true; 
} 

bearbeitet: Als eine andere Antwort offenbart hat, dieses Problem war kein Sperr Problem, aber ich verlasse diese Antwort hier als ein Ding mit Multi-Thread-Synchronisationsschemata zu überprüfen.

Die volatile Variable volatile würde auch in vielen Fällen funktionieren, aber es ist komplexer, sich als korrekt zu erweisen, weil es keine vollständigen Zäune ausgibt.

+0

Es geht nicht um Atomarität, sondern um Speicherbarrieren. Atomarität bedeutet, dass der andere Thread alles oder nichts sehen wird. Speicherbarriere (es gibt zwei Arten davon im Spiel, aber lassen wir das zur Vereinfachung überspringen) stellt sicher, dass der "None" -Fall nicht passiert. –

+0

Es ist markiert C#, nicht Java – hatchet

+0

Hoppla. Ich sprang zwischen den Fragen. Zurückgerollt, das hier vermisst. –