web-dev-qa-db-fra.com

C #: En attente de la fin de tous les threads

J'exécute un modèle commun dans le code que j'écris, où je dois attendre que tous les threads d'un groupe se terminent, avec un délai d'expiration. Le délai d'attente est censé être le temps requis pour que les threads tous se terminent, donc faire simplement thread.Join (timeout) pour chaque thread ne fonctionnera pas, car le délai d'attente possible est alors timeout * numThreads.

En ce moment, je fais quelque chose comme ceci:

var threadFinishEvents = new List<EventWaitHandle>();

foreach (DataObject data in dataList)
{
    // Create local variables for the thread delegate
    var threadFinish = new EventWaitHandle(false, EventResetMode.ManualReset);
    threadFinishEvents.Add(threadFinish);

    var localData = (DataObject) data.Clone();
    var thread = new Thread(
        delegate()
        {
            DoThreadStuff(localData);
            threadFinish.Set();
        }
    );
    thread.Start();
}

Mutex.WaitAll(threadFinishEvents.ToArray(), timeout);

Cependant, il semble qu'il devrait y avoir un idiome plus simple pour ce genre de chose.

61
JSBձոգչ

Je pense toujours que l'utilisation de Join est plus simple. Enregistrez le temps d'achèvement prévu (comme Now + timeout), puis, en boucle, faites

if(!thread.Join(End-now))
    throw new NotFinishedInTime();
26
Martin v. Löwis

Avec .NET 4.0, je trouve System.Threading.Tasks beaucoup plus facile à utiliser. Voici la boucle spin-wait qui fonctionne de manière fiable pour moi. Il bloque le thread principal jusqu'à ce que toutes les tâches soient terminées. Il y a aussi Task.WaitAll , mais cela n'a pas toujours fonctionné pour moi.

        for (int i = 0; i < N; i++)
        {
            tasks[i] = Task.Factory.StartNew(() =>
            {               
                 DoThreadStuff(localData);
            });
        }
        while (tasks.Any(t => !t.IsCompleted)) { } //spin wait
22
T. Webster

Depuis que la question a été heurtée, je vais continuer et publier ma solution.

using (var finished = new CountdownEvent(1)) 
{ 
  for (DataObject data in dataList) 
  {   
    finished.AddCount();
    var localData = (DataObject)data.Clone(); 
    var thread = new Thread( 
        delegate() 
        {
          try
          {
            DoThreadStuff(localData); 
            threadFinish.Set();
          }
          finally
          {
            finished.Signal();
          }
        } 
    ); 
    thread.Start(); 
  }  
  finished.Signal(); 
  finished.Wait(YOUR_TIMEOUT); 
} 
9
Brian Gideon

Du haut de ma tête, pourquoi ne pas simplement Thread.Join (timeout) et supprimer le temps qu'il a fallu pour rejoindre le délai total?

// pseudo-c#:

TimeSpan timeout = timeoutPerThread * threads.Count();

foreach (Thread thread in threads)
{
    DateTime start = DateTime.Now;

    if (!thread.Join(timeout))
        throw new TimeoutException();

    timeout -= (DateTime.Now - start);
}

Edit: le code est désormais moins pseudo. Je ne comprends pas pourquoi vous modifiez une réponse -2 lorsque la réponse que vous avez modifiée +4 est exactement la même, mais moins détaillée.

8
Omer van Kloeten

Cela ne répond pas à la question (pas de timeout), mais j'ai créé une méthode d'extension très simple pour attendre tous les threads d'une collection:

using System.Collections.Generic;
using System.Threading;
namespace Extensions
{
    public static class ThreadExtension
    {
        public static void WaitAll(this IEnumerable<Thread> threads)
        {
            if(threads!=null)
            {
                foreach(Thread thread in threads)
                { thread.Join(); }
            }
        }
    }
}

Ensuite, vous appelez simplement:

List<Thread> threads=new List<Thread>();
//Add your threads to this collection
threads.WaitAll();
7
Vincent

Cela peut ne pas être une option pour vous, mais si vous pouvez utiliser l'extension parallèle pour .NET, vous pouvez utiliser Tasks au lieu des threads bruts, puis utiliser Task.WaitAll() pour attendre qu'ils se terminent .

6
Jon Norton

Je tentais de comprendre comment faire cela, mais je n'ai pas pu obtenir de réponses de Google. Je sais que c'est un vieux fil mais voici ma solution:

Utilisez la classe suivante:

class ThreadWaiter
    {
        private int _numThreads = 0;
        private int _spinTime;

        public ThreadWaiter(int SpinTime)
        {
            this._spinTime = SpinTime;
        }

        public void AddThreads(int numThreads)
        {
            _numThreads += numThreads;
        }

        public void RemoveThread()
        {
            if (_numThreads > 0)
            {
                _numThreads--;
            }
        }

        public void Wait()
        {
            while (_numThreads != 0)
            {
                System.Threading.Thread.Sleep(_spinTime);
            }
        }
    }
  1. Appelez Addthreads (int numThreads) avant d'exécuter un ou plusieurs threads.
  2. Appelez RemoveThread () une fois chacun terminé.
  3. Utilisez Wait () au point où vous voulez attendre que tous les threads se terminent avant de continuer
1
Dylan

J'ai lu le livre C # 4.0: The Complete Reference of Herbert Schildt. L'auteur utilise join pour donner une solution:

class MyThread
    {
        public int Count;
        public Thread Thrd;
        public MyThread(string name)
        {
            Count = 0;
            Thrd = new Thread(this.Run);
            Thrd.Name = name;
            Thrd.Start();
        }
        // Entry point of thread.
        void Run()
        {
            Console.WriteLine(Thrd.Name + " starting.");
            do
            {
                Thread.Sleep(500);
                Console.WriteLine("In " + Thrd.Name +
                ", Count is " + Count);
                Count++;
            } while (Count < 10);
            Console.WriteLine(Thrd.Name + " terminating.");
        }
    }
    // Use Join() to wait for threads to end.
    class JoinThreads
    {
        static void Main()
        {
            Console.WriteLine("Main thread starting.");
            // Construct three threads.
            MyThread mt1 = new MyThread("Child #1");
            MyThread mt2 = new MyThread("Child #2");
            MyThread mt3 = new MyThread("Child #3");
            mt1.Thrd.Join();
            Console.WriteLine("Child #1 joined.");
            mt2.Thrd.Join();
            Console.WriteLine("Child #2 joined.");
            mt3.Thrd.Join();
            Console.WriteLine("Child #3 joined.");
            Console.WriteLine("Main thread ending.");
            Console.ReadKey();
        }
    }
1
Bùi Công Giao

Solution possible:

var tasks = dataList
    .Select(data => Task.Factory.StartNew(arg => DoThreadStuff(data), TaskContinuationOptions.LongRunning | TaskContinuationOptions.PreferFairness))
    .ToArray();

var timeout = TimeSpan.FromMinutes(1);
Task.WaitAll(tasks, timeout);

En supposant que dataList est la liste des éléments et chaque élément doit être traité dans un thread séparé.

0
Alex Aza