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, 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:我同意简短的没有答案。 这段代码没用; 你无法打破收益率循环。