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

  1. 您不应将Parallel.ForEach的操作声明为async 。 如果在该操作中使用await ,则控制流将返回到Parallel.ForEach并且其实现“认为”该操作已完成 。 这将导致与您期望的非常不同的行为

  2. 循环完成后,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 voidForEach无法判断函数何时“完成”所以它只会认为它是在你第一次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(); }