Parallel.Foreach + return return?

Je veux traiter quelque chose en utilisant une boucle parallèle comme ceci:

public void FillLogs(IEnumerable computers) { Parallel.ForEach(computers, cpt=> { cpt.Logs = cpt.GetRawLogs().ToList(); }); } 

Ok, ça marche bien. Mais comment faire si je veux que la méthode FillLogs renvoie un IEnumerable?

 public IEnumerable FillLogs(IEnumerable computers) { Parallel.ForEach(computers, cpt=> { cpt.Logs = cpt.GetRawLogs().ToList(); yield return cpt // KO, don't work }); } 

MODIFIER

Cela ne semble pas possible … mais j’utilise quelque chose comme ceci:

 public IEnumerable FillLogs(IEnumerable computers) { return computers.AsParallel().Select(cpt => cpt); } 

Mais où je mets le cpt.Logs = cpt.GetRawLogs().ToList(); instruction

Version courte – non, ce n’est pas possible via un bloc d’iterators; la version la plus longue implique probablement une queue / une queue synchronisée entre le thread iterator de l’appelant (faisant la queue) et les travailleurs parallèles (faisant la queue); mais en passant, les journaux sont généralement liés à IO, et la mise en parallèle de choses liées à IO ne fonctionne souvent pas très bien.

Si l’appelant prend un certain temps pour consumr chacun d’eux, une approche qui ne traite qu’un journal à la fois, mais peut le faire tant que l’appelant consum le journal précédent, peut présenter des avantages. c’est-à-dire qu’il commence une Task pour le prochain élément avant le yield et attend son achèvement après le yield … mais là encore, c’est assez complexe. À titre d’exemple simplifié:

 static void Main() { foreach(ssortingng s in Get()) { Console.WriteLine(s); } } static IEnumerable Get() { var source = new[] {1, 2, 3, 4, 5}; Task outstandingItem = null; Func transform = x => ProcessItem((int) x); foreach(var item in source) { var tmp = outstandingItem; // note: passed in as "state", not captured, so not a foreach/capture bug outstandingItem = new Task(transform, item); outstandingItem.Start(); if (tmp != null) yield return tmp.Result; } if (outstandingItem != null) yield return outstandingItem.Result; } static ssortingng ProcessItem(int i) { return i.ToSsortingng(); } 

Je ne veux pas être offensant, mais il y a peut-être un manque de compréhension. Parallel.ForEach signifie que le TPL exécutera le foreach en fonction du matériel disponible dans plusieurs threads. Mais cela signifie qu’il est possible de faire ce travail en parallèle! yield return vous donne la possibilité d’extraire certaines valeurs d’une liste (ou quoi que ce soit d’autre) et de les restituer une par une selon les besoins. Cela évite d’avoir à rechercher d’abord tous les éléments correspondant à la condition, puis à les parcourir. C’est un avantage en termes de performances, mais ne peut pas être réalisé en parallèle.

Que diriez-vous

  Queue qu = new Queue(); bool finished = false; Task.Factory.StartNew(() => { Parallel.ForEach(get_list(), (item) => { ssortingng itemToReturn = heavyWorkOnItem(item); lock (qu) qu.Enqueue(itemToReturn ); }); finished = true; }); while (!finished) { lock (qu) while (qu.Count > 0) yield return qu.Dequeue(); //maybe a thread sleep here? } 

Edit: Je pense que c’est mieux:

  public static IEnumerable ParallelYieldReturn(this IEnumerable source, Func func) { ConcurrentQueue qu = new ConcurrentQueue(); bool finished = false; AutoResetEvent re = new AutoResetEvent(false); Task.Factory.StartNew(() => { Parallel.ForEach(source, (item) => { qu.Enqueue(func(item)); re.Set(); }); finished = true; re.Set(); }); while (!finished) { re.WaitOne(); while (qu.Count > 0) { TOutput res; if (qu.TryDequeue(out res)) yield return res; } } } 

Edit2: Je suis d’accord avec le court Pas de réponse. Ce code est inutile. vous ne pouvez pas briser la boucle de rendement.