40 votes

Attendre que les threads du pool se terminent

Je suis désolé pour cette question redondante. Cependant, j'ai trouvé de nombreuses solutions à mon problème mais aucune n'est très bien expliquée. J'espère que ce sera clair, ici.

Le thread principal de mon application C# génère 1..n travailleurs en arrière-plan en utilisant le ThreadPool. Je souhaite que le thread d'origine se verrouille jusqu'à ce que tous les travailleurs aient terminé. J'ai fait des recherches sur le ManualResetEvent en particulier, mais je ne comprends pas bien son utilité.

En pseudo :

foreach( var o in collection )
{
  queue new worker(o);
}

while( workers not completed ) { continue; }

Si nécessaire, je connaîtrai à l'avance le nombre de travailleurs qui vont être mis en file d'attente.

0 votes

Bonjour, consultez un article similaire ici stackoverflow.com/questions/358721/

3voto

zvolkov Points 9673

Consultez l'article de mon blog pour une comparaison des différentes techniques :

http://www.zvolkov.com/clog/2009/07/10/net-4-0-and-better-ways-to-wait-for-queued-threads-to-complete/

1voto

James Points 7234

Je pense que vous étiez sur la bonne voie avec le ManualResetEvent. C'est lien a un exemple de code qui correspond étroitement à ce que vous essayez de faire. La clé est d'utiliser WaitHandle.WaitAll et de passer un tableau d'événements d'attente. Chaque thread doit définir un de ces événements d'attente.

   // Simultaneously calculate the terms.
    ThreadPool.QueueUserWorkItem(
        new WaitCallback(CalculateBase));
    ThreadPool.QueueUserWorkItem(
        new WaitCallback(CalculateFirstTerm));
    ThreadPool.QueueUserWorkItem(
        new WaitCallback(CalculateSecondTerm));
    ThreadPool.QueueUserWorkItem(
        new WaitCallback(CalculateThirdTerm));

    // Wait for all of the terms to be calculated.
    WaitHandle.WaitAll(autoEvents);

    // Reset the wait handle for the next calculation.
    manualEvent.Reset();

Edit :

Assurez-vous que dans le chemin de code de votre fil de travail, vous définissez l'événement (c.-à-d. autoEvents 1 .Set() ;). Une fois qu'ils sont tous signalés, le waitAll revient.

void CalculateSecondTerm(object stateInfo)
{
    double preCalc = randomGenerator.NextDouble();
    manualEvent.WaitOne();
    secondTerm = preCalc * baseNumber * 
        randomGenerator.NextDouble();
    autoEvents[1].Set();
}

1voto

J'ai trouvé une bonne solution ici :

http://msdn.microsoft.com/en-us/magazine/cc163914.aspx

Cela peut s'avérer utile pour d'autres personnes ayant le même problème.

1voto

Joseph Kingry Points 4594

Utilisation de .NET 4.0 Barrie r classe :

        Barrier sync = new Barrier(1);

        foreach(var o in collection)
        {
            WaitCallback worker = (state) => 
            {
                // do work
                sync.SignalAndWait();
            };

            sync.AddParticipant();
            ThreadPool.QueueUserWorkItem(worker, o);
        }

        sync.SignalAndWait();

1voto

nikoo28 Points 431

Essayez d'utiliser CountdownEvent

// code before the threads start

CountdownEvent countdown = new CountdownEvent(collection.Length);

foreach (var o in collection)
{
    ThreadPool.QueueUserWorkItem(delegate
    {
        // do something with the worker
        Console.WriteLine("Thread Done!");
        countdown.Signal();
    });
}
countdown.Wait();

Console.WriteLine("Job Done!");

// resume the code here

Le compte à rebours attendrait que tous les threads aient terminé leur exécution.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X