191 votes

Comment attendre que le thread se termine avec .NET?

Je n'ai jamais vraiment utilisé le threading auparavant dans C # où j'ai besoin d'avoir deux threads, ainsi que le thread principal de l'interface utilisateur. Fondamentalement, j'ai les éléments suivants.

 public void StartTheActions()
{
  //Starting thread 1....
  Thread t1 = new Thread(new ThreadStart(action1));
  t1.Start();

  // Now, I want for the main thread (which is calling `StartTheActions` method) 
  // to wait for `t1` to finish. I've created an event in `action1` for this. 
  // The I wish `t2` to start...

  Thread t2 = new Thread(new ThreadStart(action2));
  t2.Start();
}
 

Donc, essentiellement, ma question est de savoir comment attendre qu’un fil se termine. Quelle est la meilleure façon de procéder?

278voto

Chris S Points 32376

Je peux voir les 5 options disponibles:

1. Fil de discussion.Rejoindre

Comme avec Mitch réponse. Mais cela va bloquer votre thread de l'INTERFACE utilisateur, cependant, vous obtenez un Délai d'attente construit pour vous.


2. Utiliser un WaitHandle

ManualResetEvent est WaitHandle comme jrista suggéré.

Une chose à noter est que si vous voulez attendre plusieurs threads, WaitHandle.WaitAll() ne fonctionne pas par défaut, car il a besoin d'un thread MTA. Vous pouvez contourner ce problème en marquant votre Main() méthode de MTAThread - toutefois cela bloque votre pompe de message et n'est pas recommandé à partir de ce que j'ai lu.


3. Le feu à un événement

Voir cette page par Jon Skeet sur les événements et le multi-threading, il est possible qu'un événement peut être unsuscribed entre l' if et de la EventName(this,EventArgs.Empty) - c'est arrivé à moi avant.

(J'espère que ces compiler, je n'ai pas essayé)

public class Form1 : Form
{
    int _count;

    void ButtonClick(object sender, EventArgs e)
    {
        ThreadWorker worker = new ThreadWorker();
        worker.ThreadDone += HandleThreadDone;

        Thread thread1 = new Thread(worker.Run);
        thread1.Start();

        _count = 1;
    }

    void HandleThreadDone(object sender, EventArgs e)
    {
        // You should get the idea this is just an example
        if (_count == 1)
        {
            ThreadWorker worker = new ThreadWorker();
            worker.ThreadDone += HandleThreadDone;

            Thread thread2 = new Thread(worker.Run);
            thread2.Start();

            _count++;
        }
    }

    class ThreadWorker
    {
        public event EventHandler ThreadDone;

        public void Run()
        {
            // Do a task

            if (ThreadDone != null)
                ThreadDone(this, EventArgs.Empty);
        }
    }
}

4. L'utilisation d'un délégué

public class Form1 : Form
{
    int _count;

    void ButtonClick(object sender, EventArgs e)
    {
        ThreadWorker worker = new ThreadWorker(HandleThreadDone);

        Thread thread1 = new Thread(worker.Run);
        thread1.Start();

        _count = 1;
    }

    void HandleThreadDone()
    {
        // As before - just a simple example
        if (_count == 1)
        {
            ThreadWorker worker = new ThreadWorker();

            Thread thread2 = new Thread(worker.Run);
            thread2.Start(HandleThreadDone);

            _count++;
        }
    }

    class ThreadWorker
    {
        // Switch to your favourite Action<T> or Func<T>
        public void Run(object state)
        {
            // Do a task

            Action completeAction = (Action)state;
            completeAction.Invoke();
        }
    }
}

Si vous utilisez le _count méthode, il pourrait être une idée (pour être sûr) d'un accroissement de l'aide

Interlocked.Increment(ref _count)

Je serais curieux de savoir la différence entre les délégués et les événements pour le fil de notification, à la seule différence que je connais sont des événements sont appelés de manière synchrone.


5. Le faire de façon asynchrone à la place

La réponse à cette question a une description très claire de vos options avec cette méthode.


Délégué/Événements sur le mauvais fil

L'événement/de la délégué façon de faire les choses vont dire que votre gestionnaire d'événement de la méthode est sur thread1/thread2 pas la principale thread d'INTERFACE utilisateur, de sorte que vous aurez besoin pour passer en arrière à droite en haut de la HandleThreadDone méthodes:

// Delegate example
if (InvokeRequired)
{
    Invoke(new Action(HandleThreadDone));
    return;
}

68voto

Mitch Wheat Points 169614

Ajouter

 t1.Join();    // Wait until thread t1 finishes
 

après l'avoir démarré, mais cela n'aboutira pas à grand chose car il est essentiel d'obtenir le même résultat que sur le thread principal!

Je vous recommande vivement de lire Threading in C # free book de Joe Albahari, si vous voulez comprendre le threading dans .NET.

36voto

jrista Points 20950

Les deux dernières réponses sont grands, et travaillera pour les scénarios simples. Il y a d'autres manières de synchroniser les threads, cependant. La suite sera aussi le travail:

public void StartTheActions()
{
    ManualResetEvent syncEvent = new ManualResetEvent(false);

    Thread t1 = new Thread(
        () =>
        {
            // Do some work...
            syncEvent.Set();
        }
    );
    t1.Start();

    Thread t2 = new Thread(
        () =>
        {
            syncEvent.WaitOne();

            // Do some work...
        }
    );
    t2.Start();
}

ManualResetEvent est l'un des divers WaitHandle's que l' .NET framework a à offrir. Ils peuvent fournir beaucoup plus riche de synchronisation de thread de fonctionnalités que le simple mais très commun d'outils comme lock()/Moniteur, Fil.Joindre, etc. Ils peuvent également être utilisés pour synchroniser plus de deux threads, permettant à des scénarios complexes comme un "maître" thread qui coordonne plusieurs "enfant", threads, plusieurs processus simultanés qui dépendent de plusieurs étapes les unes des autres pour être synchronisé, etc.

5voto

Daniel Pryden Points 22167

Vous voulez la méthode Thread.Join() ou une de ses surcharges .

4voto

Ed Power Points 3179

Je voudrais que votre thread principal transmette une méthode de rappel à votre premier thread et, une fois terminé, il invoque la méthode de rappel sur le mainthread, qui peut lancer le second thread. Cela empêche votre thread principal de se bloquer pendant qu'il attend une jointure ou un waithandle. Passer des méthodes en tant que délégués est une chose utile à apprendre avec C # de toute façon.

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