How do I add a finalizer that runs once all parallels have completed?
Parallel.ForEach(entries,newParallelOptions{MaxDegreeOfParallelism=15}, async (entry)=>// Do something with the entry.});
I have tried like this but it doesn't compile:
Parallel.ForEach(entries,newParallelOptions{MaxDegreeOfParallelism=15}, async (entry)=>// Do something with the entry.},()=>{// Was hoping this would work. });
回答1:
You should not declare the action for the Parallel.ForEach as async. If you use an await inside that action, the control flow is returned to Parallel.ForEach and its implementation "thinks" that the action is finished. This will lead to a very different behaviour than you expect.
The call to Parallel.ForEach returns when the loop is completed. It returns when all actions have been done for all the elements in the enumeration. So whatever you want to do "when all parallels have completed" can be done right after that call:
Parallel.ForEach(entries,newParallelOptions{MaxDegreeOfParallelism=15},(entry)=>// Do something with the entry.);DoSomethingWhenAllParallelsHaveCompleted();
回答2:
You dont have to do anything. Your Parallel.ForEach will run until all threads have finished their work. Thats one of the really nice benefits the Parallel.Foreach() has.
So right after Parallel.ForEach(() => { /* code */ }); all threads will be finished.
回答3:
As I and others mentioned in comments Parallel.ForEach does not support async functions the reason is when you did async (entry) => ... that is the same as
Because the function is async void the ForEach can't tell when a function is "done" so it will just think it is done when you hit the first await instead of when the task finishes.
The way to fix this is to use a library that can suport async functions, TPL Dataflow is a good one. You get it by installing the NuGet package to your project Microsoft.Tpl.Dataflow. You could recreate your previous code as
privateconstint MAX_PARALLELISM =15public async TaskProcessEntries(IEnumerable<Entry> entries){var block =newActionBlock<Entry>(async (entry)=>{//This is now a "async Task" instead of a async void},newExecutionDataflowBlockOptions{MaxDegreeOfParallelism= MAX_PARALLELISM });foreach(var entry in entries){ await block.SendAsync(entry);} block.Complete(); await block.Completion;DoExtraWorkWhenDone();}