Parallel .ForEach vs Task.Run and Task.WhenAll

What is the difference between using Parallel.ForEach or Task.Run () to asynchronously run a task set?

Version 1:

List<string> strings = new List<string> { "s1", "s2", "s3" }; Parallel.ForEach(strings, s => { DoSomething(s); }); 

Version 2:

 List<string> strings = new List<string> { "s1", "s2", "s3" }; List<Task> Tasks = new List<Task>(); foreach (var s in strings) { Tasks.Add(Task.Run(() => DoSomething(s))); } await Task.WhenAll(Tasks); 
+136
c # async-await parallel.foreach
Sep 30 '13 at 20:13
source share
4 answers

In this case, the second method will asynchronously wait for the completion of tasks instead of locking.

However, there is a drawback to using Task.Run in a loop. With Parallel.ForEach there is a Partitioner that is created so as not to create more tasks than necessary. Task.Run will always do one task for each element (since you do this), but Parallel class packages work, so you create fewer tasks than common work items. This can provide significantly better overall performance, especially if the loop body has a small amount of work for each element.

If so, you can combine both parameters by writing:

 await Task.Run(() => Parallel.ForEach(strings, s => { DoSomething(s); })); 

Please note that this can also be written in this shorter form:

 await Task.Run(() => Parallel.ForEach(strings, DoSomething)); 
+132
Sep 30 '13 at 20:17
source share

The first version will synchronously block the calling thread (and perform some of the tasks on it).
If it is a user interface thread, it will freeze the user interface.

The second version will start tasks asynchronously in the thread pool and release the calling thread until they are completed.

There are also differences in the planning algorithms used.

Please note that your second example can be shortened to

 await Task.WhenAll(strings.Select(s => Task.Run(() => DoSomething(s))); 
+31
Sep 30 '13 at 20:15
source share

I ended up doing this as it was easier to read:

  List<Task> x = new List<Task>(); foreach(var s in myCollectionOfObject) { // Note there is no await here. Just collection the Tasks x.Add(s.DoSomethingAsync()); } await Task.WhenAll(x); 
0
Apr 14 '18 at 2:21
source share

I saw that Parallel.ForEach was being used improperly, and I thought an example in this question would help.

When you run the code below in a console application, you will see how the tasks performed in Parallel.ForEach do not block the calling thread. This may be good if you do not care about the result (positive or negative), but if you need a result, you should definitely use Task.WhenAll.

 using System; using System.Linq; using System.Threading.Tasks; namespace ParrellelEachExample { class Program { static void Main(string[] args) { var indexes = new int[] { 1, 2, 3 }; RunExample((prefix) => Parallel.ForEach(indexes, (i) => DoSomethingAsync(i, prefix)), "Parallel.Foreach"); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("*You'll notice the tasks haven't run yet, because the main thread was not blocked*"); Console.WriteLine("Press any key to start the next example..."); Console.ReadKey(); RunExample((prefix) => Task.WhenAll(indexes.Select(i => DoSomethingAsync(i, prefix)).ToArray()).Wait(), "Task.WhenAll"); Console.WriteLine("All tasks are done. Press any key to close..."); Console.ReadKey(); } static void RunExample(Action<string> action, string prefix) { Console.ForegroundColor = ConsoleColor.White; Console.WriteLine($"{Environment.NewLine}Starting '{prefix}'..."); action(prefix); Console.WriteLine($"{Environment.NewLine}Finished '{prefix}'{Environment.NewLine}"); } static async Task DoSomethingAsync(int i, string prefix) { await Task.Delay(i * 1000); Console.WriteLine($"Finished: {prefix}[{i}]"); } } } 

Here is the result:

enter image description here

Conclusion:

Using Parallel.ForEach with a task does not block the calling thread. If you care about the result, be sure to wait for the assignment.

~ Greetings

0
Jul 18 '19 at 0:54
source share



All Articles