2010-02-22 11 views
8

Ich versuche, einen ThreadManager für meine C# -Anwendung zu schreiben. Ich erstelle mehrere Threads:
Ein Thread für meinen Textschreiber.
Ein Thread, der einige Statistiken überwacht.
Mehrere Threads, um eine große Reihe von Berechnungen durchzuführen (bis zu 4 Threads pro Kern und ich meine App auf einem 2x Quad-Core-Server laufen).Herunterfahren einer Multithread-Anwendung

Meine Anwendung läuft normalerweise für bis zu 24 Stunden auf einmal, so dass alle Threads am Anfang erstellt werden und sie bestehen während der gesamten Zeit, die die App ausgeführt wird.

Ich möchte einen einzigen Ort haben, an dem ich alle meine Schritte "registriere" und wenn die Anwendung heruntergefahren wird, rufe ich einfach eine Methode auf und sie durchläuft alle registrierten Threads und schließt sie ab.

Zu diesem Zweck ich die folgende Klasse entwickelt haben:

public class ThreadManager 
{ 
    private static Object _sync = new Object(); 
    private static ThreadManager _instance = null; 
    private static List<Thread> _threads; 
    private ThreadManager() 
    { 
     _threads = new List<Thread>(); 
    } 

    public static ThreadManager Instance 
    { 
     get 
     { 
      lock (_sync) 
      { 
       if (_instance == null) 
       { 
        _instance = new ThreadManager(); 
       } 
      } 
      return _instance; 
     } 
    } 

    public void AddThread(Thread t) 
    { 
     lock (_sync) 
     { 
      _threads.Add(t); 
     } 
    } 

    public void Shutdown() 
    { 
     lock (_sync) 
     { 
      foreach (Thread t in _threads) 
      { 
       t.Abort(); // does this also abort threads that are currently blocking? 
      } 
     } 
    } 
} 

Ich möchte sicherstellen, dass alle meine Fäden getötet werden, so kann die Anwendung richtig schließen und in der Mitte einige Berechnung heruntergefahren ist gerade fein auch. Sollte mir hier etwas bekannt sein? Ist dieser Ansatz angesichts meiner Situation gut?

+0

WOW! Viele gute Antworten ... es ist schwer zu wählen. Ich gab Nate das Häkchen, weil er der Erste war, und es wäre ungerecht, ihn ihm wegzunehmen. Mal sehen, was die Stimmen sagen. Ich bekomme etwas nützliches von all deinen Antworten, also stimme bitte mit deinen Herzen. – Kiril

+0

Das Standardverhalten von Thread-Pool-Threads entspricht dem von mir vorgeschlagenen. Wenn es dir egal ist, dass deine Threads nicht zu Ende gehen, ist meins die einfachste Lösung. Offensichtlich machen die anderen Antworten gute Argumente für die Verwaltung von Threads, aber Sie geben explizit an, dass das Töten von Threads mitten in einer Berechnung in Ordnung ist. –

+0

Ich stimme dir zu Nate;), ich werde das Häkchen nicht wegnehmen, weil es unfair wäre und du mir die genaueste, schnelle und einfache Antwort gegeben hast ... Ich möchte, dass andere Leute fair werden Kredit, also möchte ich, dass jeder für alle guten Antworten stimmt. – Kiril

Antwort

16

Wenn Sie die Threads auf Hintergrundthreads setzen, werden sie beim Herunterfahren der Anwendung gelöscht.

offensichtlich, wenn Sie die Threads vor dem Herunterfahren beenden müssen, ist dies nicht die Lösung, die Sie möchten.

+0

Danke Nate, ich setze alle meine Threads auf Hintergrund-Threads, wenn ich sie erstelle ... Bedeutet das, dass ich sie überhaupt nicht abbrechen muss? – Kiril

+0

Ja, wenn sie auf Hintergrundthreads gesetzt sind, wird beim Schließen der Anwendung das Beenden der Threads erzwungen. http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground.aspx –

2

Die einzige spezifische Frage, die ich über wissen, ist dieser: http://www.bluebytesoftware.com/blog/2007/01/30/MonitorEnterThreadAbortsAndOrphanedLocks.aspx

Aber ich würde vermeiden, wie dies zu einem Design zu müssen. Sie könnten jeden Ihrer Threads dazu zwingen, einige Flags regelmäßig zu überprüfen, dass es Zeit für das Herunterfahren ist, und wenn Sie herunterfahren, setzen Sie dieses Flag und warten Sie, bis alle Threads beendet sind (mit Join()). Es fühlt sich ein bisschen mehr wie kontrolliertes Herunterfahren an.

14

Abbrechen von Threads ist das, was Sie tun , wenn alles andere fehlschlägt. Es ist eine gefährliche Sache, die Sie nur als letzten Ausweg tun sollten. Die richtige Vorgehensweise besteht darin, die Threading-Logik so zu gestalten, dass jeder Worker-Thread schnell und korrekt reagiert, wenn der Haupt-Thread den Befehl zum Herunterfahren gibt.

Zufälligerweise ist dies das Thema meines Blogs diese Woche.

http://blogs.msdn.com/ericlippert/archive/2010/02/22/should-i-specify-a-timeout.aspx

+1

Die Thread-Abbruch-Ausnahmen werden während der Ausführung von endlich blockiert blockiert, ist neu für mich. Danke, dass du das hinzugefügt hast. –

+0

@ fatcat1111: In der Tat garantiert nichts, dass ein abgebrochener Thread * jemals * als Ergebnis des Abbruchs stoppt. Nur ein weiterer Grund, Thread.Abort zu vermeiden; es ist sowohl gefährlich als auch unzuverlässig. –

+0

Einverstanden, definitiv eine letzte Möglichkeit. –

3

Was passiert, wenn AddThread während Shutdown ausgeführt wird, aufgerufen wird?

Wenn das Herunterfahren beendet ist, fügt der Thread, der in AddThread wartet, der Sammlung einen neuen Thread hinzu. Dies könnte dazu führen, dass Sie in Ihrer App hängen bleiben.

Fügen Sie ein bool-Flag hinzu, das Sie nur in Shutdown festgelegt haben, um dies zu verhindern.

bool shouldGoAway = false; 
public void AddThread(Thread t) 
{ 
    lock (_sync) 
    { 
     if(! shouldGoAway) 
      _threads.Add(t); 
    } 
} 

public void Shutdown() 
{ 
    lock (_sync) 
    { 
     shouldGoAway = true; 
     foreach (Thread t in _threads) 
     { 
      t.Abort(); // does this also abort threads that are currently blocking? 
     } 
    } 

Auch sollten Sie keine statischen Elemente verwenden - es gibt keinen Grund dafür ist, wie Sie Ihre Singleton Instanz haben.

.Abort() bricht Threads, die im nicht verwalteten Speicherbereich blockieren, nicht ab.Wenn Sie das tun, müssen Sie einen anderen Mechanismus verwenden.

1

Wenn Sie nicht über den Arbeitsthread Zustand kümmern, dann können Sie eine Schleife durch _thread und Abbruch:

void DieDieDie() 
{ 
    foreach (Thread thread in _thread) 
    { 
     thread.Abort(); 
     thread.Join(); // if you need to wait for the thread to die 
    } 
} 

In Ihrem Fall können Sie wahrscheinlich sie einfach abbrechen alles und Herunterfahren wie sie sind nur Berechnungen zu tun . Wenn Sie jedoch auf einen Datenbankschreibvorgang warten müssen oder eine nicht verwaltete Ressource schließen müssen, müssen Sie entweder die ThreadAbortException abfangen oder den Threads signalisieren, dass sie sich selbst töten.

0

Sie wollen deferred Thread Cancellation, was im Grunde bedeutet, dass die Threads sich selbst beenden, im Gegensatz zu einem Thread-Manager, der Threads asynchron abbricht, was viel schlechter definiert und gefährlich ist.

Wenn Sie Thread-Löschung eleganter als sofortige Beendigung behandeln möchten, können Sie Signal-Handler verwenden, die durch Ereignisse außerhalb des Threads ausgelöst werden - möglicherweise von Ihrem Thread-Manager.