2009-07-31 8 views
5

J'écris un programme dans lequel je commence généralement cinq threads. Les threads reviennent dans un ordre non déterminé. Chaque thread appelle une méthode qui renvoie une liste.Fils qui retournent des données dans .NET

Je fais ceci:

var masterList = List<string>();  
foreach (var threadParam in threadParams) 
{ 
    var expression = threadParam ; 
    ThreadStart sub =() => MyMethod(expressions); 
    var thread = new Thread(sub) 
    { 
     Name = expression 
    }; 

    listThreads.Add(thread); 
    thread.Start(); 
} 

var abort = true; 
while (abort) //Wait until all threads finish 
{ 
    var count = 0; 
    foreach (var list in listThreads) 
    { 
     if (!list.IsAlive) 
     { 
      count++; 
     } 
    } 

    if (count == listThreads.Count) 
    { 
     abort = false; 
    } 
} 

Voici donc le problème:

Chaque thread quand il se termine retourne une liste que je voudrais ajouter le MasterList déclaré plus tôt.

Comment peut-on y arriver?

Aussi je sais qu'il doit y avoir une meilleure façon que ci-dessous pour attendre toutes les discussions pour terminer

var abort = true; 
while (abort) //Wait until all threads finish 
{ 
    var count = 0; 
    foreach (var list in listThreads) 
    { 
     if (!list.IsAlive) 
     { 
      count++; 
     } 
    } 

    if (count == listThreads.Count) 
    { 
     abort = false; 
    } 
} 

Répondre

14

Utilisez un WaitHandle


Voici un exemple:

using System; 
using System.Threading; 

class ThreadSleeper 
{ 
    int seconds; 
    AutoResetEvent napDone = new AutoResetEvent(false); 

    private ThreadSleeper(int seconds) 
    { 
     this.seconds = seconds; 
    } 

    public void Nap() 
    { 
     Console.WriteLine("Napping {0} seconds", seconds); 
     Thread.Sleep(seconds * 1000); 
     Console.WriteLine("{0} second nap finished", seconds); 
     napDone.Set(); 
    } 

    public static WaitHandle DoSleep(int seconds) 
    { 
     ThreadSleeper ts = new ThreadSleeper(seconds); 
     Thread thread = new Thread(new ThreadStart(ts.Nap)); 
     thread.Start(); 
     return(ts.napDone); 
    } 
} 

public class OperationsThreadsWaitingwithWaitHandle 
{ 
    public static void Main() 
    { 
     WaitHandle[] waits = new WaitHandle[2]; 
     waits[0] = ThreadSleeper.DoSleep(8); 
     waits[1] = ThreadSleeper.DoSleep(4); 

     Console.WriteLine("Waiting for threads to finish"); 
     WaitHandle.WaitAll(waits); 
     Console.WriteLine("Threads finished"); 
    } 
} 

Liens pour vérifier:

+1

Même idée que la mienne ... mais une meilleure façon de répondre :( –

0

Vérifiez la classe WaitHandle et la WaitHandle.WaitAll (je l'ai utilisé la classe ManualResetEvent dans certaines mon code pour le WaitHandle mais c'est juste à partir d'un exemple, je ne sais pas s'il y a quelque chose de mieux pour votre situation). Lancez les cinq threads, donnez-leur une référence à votre liste principale, verrouillez cette liste en l'ajoutant, puis complétez le signal. Utilisez WaitHandle.WaitAll pour bloquer jusqu'à ce que tous les cinq ont signalé l'achèvement.

2

Le meilleur moyen serait de faire de chaque thread son propre objet. N'ayant aucun mélange avec d'autres objets, il suffit de le construire (en passant dans les variables), de vous ajouter en tant qu'auditeur et de le démarrer. Quand c'est fait, il stocke les valeurs dans une variable membre et notifie votre écouteur.

Votre auditeur peut récupérer les valeurs à loisir.

Le raccourci évident, retourner les valeurs directement à l'auditeur, fonctionne mais vous pouvez trouver cette version plus souple plus tard (et vraiment pas beaucoup plus de code)

1

Vous pouvez bien sûr utiliser des délégués réguliers ainsi que APM.

Notez que le motif que vous décrivez est normalement décrit comme Future, un travail d'arrière-plan qui promet de retourner quelque chose plus tard (un retour sur investissement si vous le pouvez).

Voici un petit exemple, sous la forme d'un programme de console.

Sortie:

sequential: 3224 ms 
parallel: 2074 ms 

Bien sûr, puisque je ne suis pas la construction de fils explicites, je laisse au système ThreadPool pour savoir combien de threads pour exécuter en parallèle.

Il y a aussi des dispositions pour être informé du moment où les discussions de fond sont terminés, via une méthode de rappel, ainsi que le soutien WaitHandle de vérifier explicitement et en attente avec délai d'attente, etc.

Et la source:

using System; 
using System.Collections.Generic; 
using System.Threading; 
using System.Diagnostics; 

namespace SO1215227 
{ 
    public class Program 
    { 
     public static void Main() 
     { 
      Stopwatch sw = new Stopwatch(); 
      sw.Start(); 
      var list1 = Sequence(1, 100); 
      var list2 = Sequence(101, 200); 
      var list3 = Sequence(201, 300); 
      sw.Stop(); 
      Console.Out.WriteLine("sequential: " + sw.ElapsedMilliseconds + " ms"); 
      sw.Reset(); 

      Func<Int32, Int32, List<Int32>> listProducer = Sequence; 
      sw.Start(); 
      var list1Background = listProducer.BeginInvoke(1, 100, null, null); 
      var list2Background = listProducer.BeginInvoke(101, 200, null, null); 
      var list3Background = listProducer.BeginInvoke(201, 300, null, null); 

      list1 = listProducer.EndInvoke(list1Background); 
      list2 = listProducer.EndInvoke(list2Background); 
      list3 = listProducer.EndInvoke(list3Background); 
      sw.Stop(); 
      Console.Out.WriteLine("parallel: " + sw.ElapsedMilliseconds + " ms"); 

      Console.Out.Write("Press enter to exit..."); 
      Console.In.ReadLine(); 
     } 

     private static List<Int32> Sequence(Int32 from, Int32 to) 
     { 
      List<Int32> result = new List<Int32>(); 
      for (Int32 index = from; index <= to; index++) 
      { 
       result.Add(index); 
       Thread.Sleep(10); // simulate I/O wait 
      } 
      return result; 
     } 
    } 
} 
Questions connexes