2008-12-11 12 views
10

fertig ist habe ich ein Szenario, wenn ich 3..10 Threads mit Threadpool starten. Jeder Thread erledigt seine Aufgabe und kehrt zum ThreadPool zurück. Welche Optionen können im Hauptthread angezeigt werden, wenn alle Hintergrundthreads beendet sind?benachrichtigt werden, wenn alle Hintergrund Thread Fäden

zurzeit homegrown Methode mit eine Variable für jede der erstellten Threads Erhöhen und Erniedrigen es ich verwende, wenn ein Hintergrundthread zu beenden ist etwa. Das funktioniert ganz gut, aber ich war neugierig, ob es bessere Möglichkeiten gibt.

Antwort

11

Dekrementieren einer Variable (zwischen Threads) ist ein wenig riskant, wenn nicht mit Interlocked.Decrement getan, aber diese Methode sollte in Ordnung sein, wenn Sie den letzten Thread (d. H. Wenn es auf Null kommt) ein Ereignis auslösen. Beachten Sie, dass es muß in einem „endlich“ Block wäre zu vermeiden, ist es im Fall von Ausnahmen zu verlieren (plus Sie nicht wollen, um den Prozess zu töten).

In "Parallel Extensions" (oder mit .NET 4.0), könnten Sie auch die Parallel.ForEach Optionen hier sehen ... das könnte eine andere Möglichkeit sein, alles als Block zu erledigen. Ohne sie alle manuell beobachten zu müssen.

+0

Hallo Marc. Ich mache es in einer Thread-sicheren Weise, entweder mit Interlocked oder Lock-Schlüsselwort. Es funktioniert einwandfrei und nicht blockierend. Ich frage mich, ob es dafür eingebaute Primitive gibt. Danke. – Valentin

2

Es gibt keine integrierte Möglichkeit, dies im Moment zu tun - finde ich es eine der größten Schmerzen über Pool-Threads.

Wie Marc sagt, das ist die Art von Sachen, die in Parallel Extensions/.NET 4.0 behoben wird.

1

Konnten Sie nicht jedem Thread ein bestimmtes ManualResetEvent geben und jedes Ereignis nach Abschluss setzen. Dann können Sie im Haupt-Thread auf alle eingegangenen Ereignisse warten.

0

Marcs Lösung ist am besten, wenn Sie nur wissen wollen, wann alle Jobs fertig sind und keine feineren Informationen benötigen (wie es scheint sei dein Fall).

Wenn Sie einige Thread zu erzeugen Jobs, und einige andere Thread erhalten die Benachrichtigungen wollten, könnten Sie verwenden Waithandle. Der Code ist viel länger.

int length = 10; 
    ManualResetEvent[] waits = new ManualResetEvent[length]; 
    for (int i = 0; i < length; i++) { 
     waits[i] = new ManualResetEvent(false); 
     ThreadPool.QueueUserWorkItem((obj) => { 
      try { 

      } finally { 
       waits[i].Set(); 
      } 
     }); 
    } 

    for (int i = 0; i < length; i++) { 
     if (!waits[i].WaitOne()) 
      break; 
    } 

Die WaitOne Methode, wie geschrieben, kehrt immer wahr, aber ich habe so geschrieben, es machen Sie bedenken, dass einige Überlastungen ein Timeout als Argument nehmen.

3

Wenn ihr nicht mehr als 64 Themen auf, zu warten, können Sie die WaitHandle.WaitAll Methode wie folgt verwenden:

List<WaitHandle> events = new List<WaitHandle>(); 
for (int i = 0; i < 64; i++) 
{ 
    ManualResetEvent mre = new ManualResetEvent(false); 
    ThreadPool.QueueUserWorkItem(
     delegate(object o) 
     { 
      Thread.Sleep(TimeSpan.FromMinutes(1)); 
      ((ManualResetEvent)o).Set(); 
     },mre); 
    events.Add(mre); 
} 
WaitHandle.WaitAll(events.ToArray()); 

Die Ausführung wird warten, bis alle ManualResetEvents eingestellt sind, alternativ können Sie WaitAny Methode verwenden .

Die WaitAny- und WaitAll-Methoden blockieren die Ausführung, aber Sie können einfach die Liste oder ein Dictionary von ManualResetEvents verwenden, das mit der spawn-Task verbunden ist, um später zu ermitteln, ob der Thread erledigt ist.

+1

Diese Methode funktioniert gut, solange das Main() kein Single Thread Apartment ([STAThread]) ist. –

+0

Warum muss es "mehr als 64 Threads warten auf" sein? – jp2code

+0

Wow, diese Frage trifft ... Die Antwort ist mehr als 4 Jahre alt ... wie ich mich erinnere gibt es eine Einschränkung in der WaitHandle.WaitAll, die eine Ausnahme auslöst, wenn die Liste der Griffe mehr als 64 Elemente hat. Vielleicht ist das heute veraltet. –

0

Was ist mit der Verwendung von Semaphore, und legen Sie ein Limit so viel wie Ihr Thread-Pool. Habe eine Methode, um einen Semaphore zu holen, der aufgerufen wird, wenn du deinen Thread startest, ihn loslassen, wenn dein Thread endet und ein Event auslöst, wenn du den gesamten Semaphor benutzt hast.

4

Versuchen Sie folgendes: https://bitbucket.org/nevdelap/poolguard

using (var poolGuard = new PoolGuard()) 
{ 
    for (int i = 0; i < ... 
    { 
     ThreadPool.QueueUserWorkItem(ChildThread, poolGuard); 
    } 
    // Do stuff. 
    poolGuard.WaitOne(); 
    // Do stuff that required the child threads to have ended. 

void ChildThread(object state) 
{ 
    var poolGuard = state as PoolGuard; 
    if (poolGuard.TryEnter()) 
    { 
     try 
     { 
      // Do stuff. 
     } 
     finally 
     { 
      poolGuard.Exit(); 
     } 
    } 
} 

Multiple PoolGuards kann auf verschiedene Weise verwendet werden, um zu verfolgen, wenn Threads beendet haben, und behandelt Themen, die noch nicht begonnen haben, wenn der Pool bereits geschlossen ist.

+0

Der Artikel ist nicht mehr verfügbar. Was ist eine PoolGuard-Klasse? Könntest du den kompletten Code kopieren? – Niloofar

Verwandte Themen