Itération parallèle en C #?

Y at-il un moyen de faire pour foreach itération de style sur les énumérables parallèles en C #? Pour les listes enregistrables, je sais que l’on pourrait utiliser une boucle régulière for itérer un int sur la plage d’index, mais je préfère vraiment foreach pour plusieurs raisons.

Points bonus si cela fonctionne en C # 2.0

Réponse courte, non. foreach fonctionne que sur un énumérable à la fois.

Cependant, si vous combinez vos énumérables parallèles en un seul, vous pouvez en rechercher plus. Je ne connais pas de méthode simple et intégrée pour le faire, mais ce qui suit devrait fonctionner (même si je ne l’ai pas testée):

 public IEnumerable Combine(params object[] sources) { foreach(var o in sources) { // Choose your own exception if(!(o is IEnumerable)) throw new Exception(); } var enums = sources.Select(s => ((IEnumerable)s).GetEnumerator()) .ToArray(); while(enums.All(e => e.MoveNext())) { yield return enums.Select(e => e.Current).ToArray(); } } 

Ensuite, vous pouvez foreach à la liste énumérable renvoyée:

 foreach(var v in Combine(en1, en2, en3)) { // Remembering that v is an array of the type contained in en1, // en2 and en3. } 

BlockingCollection de .NET 4 facilite cette tâche. Créez une BlockingCollection, retournez son .GetConsumingEnumerable () dans la méthode énumérable. Ensuite, le foreach ajoute simplement à la collection de blocage.

Par exemple

 private BlockingCollection m_data = new BlockingCollection(); public IEnumerable GetData( IEnumerable> sources ) { Task.Factory.StartNew( () => ParallelGetData( sources ) ); return m_data.GetConsumingEnumerable(); } private void ParallelGetData( IEnumerable> sources ) { foreach( var source in sources ) { foreach( var item in source ) { m_data.Add( item ); }; } //Adding complete, the enumeration can stop now m_data.CompleteAdding(); } 

J’espère que cela t’aides. BTW j’ai posté un blog à ce sujet hier soir

André

La réponse de Zooba est bonne, mais vous pouvez également consulter les réponses à la section “Comment parcourir plusieurs tableaux à la fois” .

J’ai écrit une implémentation de EachParallel () à partir de la bibliothèque .NET4 Parallel. Il est compatible avec .NET 3.5: Boucle ForEach parallèle en C # 3.5

 ssortingng[] names = { "cartman", "stan", "kenny", "kyle" }; names.EachParallel(name => { try { Console.WriteLine(name); } catch { /* handle exception */ } }); 

La mise en oeuvre:

 ///  /// Enumerates through each item in a list in parallel ///  public static void EachParallel(this IEnumerable list, Action action) { // enumerate the list so it can't change during execution list = list.ToArray(); var count = list.Count(); if (count == 0) { return; } else if (count == 1) { // if there's only one element, just execute it action(list.First()); } else { // Launch each method in it's own thread const int MaxHandles = 64; for (var offset = 0; offset < list.Count() / MaxHandles; offset++) { // break up the list into 64-item chunks because of a limitiation // in WaitHandle var chunk = list.Skip(offset * MaxHandles).Take(MaxHandles); // Initialize the reset events to keep track of completed threads var resetEvents = new ManualResetEvent[chunk.Count()]; // spawn a thread for each item in the chunk int i = 0; foreach (var item in chunk) { resetEvents[i] = new ManualResetEvent(false); ThreadPool.QueueUserWorkItem(new WaitCallback((object data) => { int methodIndex = (int)((object[])data)[0]; // Execute the method and pass in the enumerated item action((T)((object[])data)[1]); // Tell the calling thread that we're done resetEvents[methodIndex].Set(); }), new object[] { i, item }); i++; } // Wait for all threads to execute WaitHandle.WaitAll(resetEvents); } } } 

Si vous voulez vous en tenir à l’essentiel, j’ai récrit de manière plus simple la réponse actuellement acceptée:

  public static IEnumerable Combine (this IEnumerable> sources) { var enums = sources .Select (s => s.GetEnumerator ()) .ToArray (); while (enums.All (e => e.MoveNext ())) { yield return enums.Select (e => e.Current).ToArray (); } } public static IEnumerable Combine (params IEnumerable[] sources) { return sources.Combine (); } 

Est-ce que cela fonctionnerait pour vous?

 public static class Parallel { public static void ForEach(IEnumerable[] sources, Action action) { foreach (var enumerable in sources) { ThreadPool.QueueUserWorkItem(source => { foreach (var item in (IEnumerable)source) action(item); }, enumerable); } } } // sample usage: static void Main() { ssortingng[] s1 = { "1", "2", "3" }; ssortingng[] s2 = { "4", "5", "6" }; IEnumerable[] sources = { s1, s2 }; Parallel.ForEach(sources, s => Console.WriteLine(s)); Thread.Sleep(0); // allow background threads to work } 

Pour C # 2.0, vous devez convertir les expressions lambda ci-dessus en delegates.

Remarque: cette méthode utilitaire utilise des threads d’arrière-plan. Vous voudrez peut-être le modifier pour utiliser les threads de premier plan et probablement attendre que tous les threads soient terminés. Si vous faites cela, je vous suggère de créer sources.Length - 1 threads et d’utiliser le thread en cours d’exécution pour la dernière (ou première) source.

(J’aimerais pouvoir inclure l’attente de la fin des threads dans mon code, mais je suis désolé de ne pas savoir comment le faire. Je suppose que vous devriez utiliser un WaitHandle Thread.Join() .)