Parallel.Foreach +收益率回报?

我想用这样的并行循环处理一些东西:

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

好的,它工作正常。 但是如果我想让FillLogs方法返回一个IEnumerable怎么办?

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

编辑

似乎不可能……但我使用这样的东西:

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

但是我放了cpt.Logs = cpt.GetRawLogs().ToList(); 指令

简短版本 – 不,这是不可能通过迭代器块; 较长的版本可能涉及调用者的迭代器线程(执行dequeue)和并行工作者(执行enqueue)之间的同步队列/出队; 但是作为旁注 – 日志通常是IO绑定的,并且对IO绑定的内容进行并行化通常不能很好地工作。

如果调用者需要花费一些时间来消耗每个,那么一次只处理一个日志的方法可能有一些优点,但是调用者使用前一个日志时可以这样做; 即它在yield之前开始 下一个项目Task ,并在yield之后等待完成……但这又是非常复杂的。 作为简化示例:

 static void Main() { foreach(string 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 string ProcessItem(int i) { return i.ToString(); } 

我不想冒犯,但也许缺乏理解。 Parallel.ForEach意味着TPL将根据多个线程中的可用硬件运行foreach。 但这意味着,ii可以并行完成这项工作! yield return使您有机会从列表中获取一些值(或者说是什么),并在需要时逐个返回。 它可以防止首先找到符合条件的所有项目,然后迭代它们。 这确实是一种性能优势,但不能并行完成。

怎么样

  Queue qu = new Queue(); bool finished = false; Task.Factory.StartNew(() => { Parallel.ForEach(get_list(), (item) => { string 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? } 

编辑:我认为这更好:

  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; } } } 

编辑2:我同意简短的没有答案。 这段代码没用; 你无法打破收益率循环。