Parallel.ForEach的终结器
如何在所有并行完成后添加运行的终结器?
Parallel.ForEach(entries, new ParallelOptions { MaxDegreeOfParallelism = 15 }, async (entry) => // Do something with the entry. });
我试过这样但它不编译:
Parallel.ForEach(entries, new ParallelOptions { MaxDegreeOfParallelism = 15 }, async (entry) => // Do something with the entry. }, () => { // Was hoping this would work. });
-
您不应将
Parallel.ForEach
的操作声明为async
。 如果在该操作中使用await
,则控制流将返回到Parallel.ForEach
并且其实现“认为”该操作已完成 。 这将导致与您期望的非常不同的行为 。 -
循环完成后,对
Parallel.ForEach
的调用将返回。 当对枚举中的所有元素执行了所有操作时,它将返回。 所以,无论你想做什么,“当所有的并行完成后”都可以在那个电话之后立即完成:Parallel.ForEach(entries, new ParallelOptions { MaxDegreeOfParallelism = 15 }, (entry) => // Do something with the entry. ); DoSomethingWhenAllParallelsHaveCompleted();
你不必做任何事情。 您的Parallel.ForEach
将一直运行,直到所有线程完成其工作。 这是Parallel.Foreach()
一个非常好的好处。
就在Parallel.ForEach(() => { /* code */ });
所有线程都将完成。
正如我和其他人在评论中提到的那样, Parallel.ForEach
不支持异步函数,原因是当你执行async (entry) => ...
它就像
Parallel.ForEach(entries, Example); //elsewhere async void Example(Entry entry) { ... }
因为函数是async void
, ForEach
无法判断函数何时“完成”所以它只会认为它是在你第一次await
而不是任务完成时完成的。
解决这个问题的方法是使用一个可以支持异步函数的库, TPL Dataflow是一个很好的函数。 您可以通过将NuGet包安装到项目Microsoft.Tpl.Dataflow
来获得它。 您可以重新创建以前的代码
private const int MAX_PARALLELISM = 15 public async Task ProcessEntries(IEnumerable entries) { var block = new ActionBlock (async (entry) => { //This is now a "async Task" instead of a async void }, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = MAX_PARALLELISM }); foreach(var entry in entries) { await block.SendAsync(entry); } block.Complete(); await block.Completion; DoExtraWorkWhenDone(); }