BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Parallel and Asynchronous
.NET 4.0+

Parallel LINQ and ForAll

The Parallel class defines loops that allow sequences of values to be enumerated in parallel. This can provide improved performance over sequential loops when the iterations are independent. When using PLINQ, the ForAll method provides such a loop.

Parallel Loops

In the Parallel Programming in .NET tutorial I described two parallel loops provided by static methods of the Parallel class. Parallel.For is an alternative to the standard for loop that executes its iterations in parallel where possible. Parallel.ForEach is a parallel version of the standard foreach loop. It performs an action upon each element of any array or sequence that implements IEnumerable<T>, with the potential for parallelism on computers with multiple processors or processor cores.

As Parallel.ForEach works with IEnumerable<T>, you can use it to run a process for most collection types. You can also use it with the results of Language-Integrated Query (LINQ) methods and queries. For example, the code below loops through all even numbers between one and fifty. If you run the code on a machine with multiple cores you are likely to see that the outputted numbers are not in order, due to parallel execution.

var values = Enumerable.Range(1, 50).Where(i => i % 2 == 0);
Parallel.ForEach(values, i => Console.WriteLine(i));

When you are using Parallel LINQ (PLINQ), you can combine a number of standard query operators using a fluent interface, adding the AsParallel method to convert the series into a ParallelQuery<T> object that supports parallel operations. If you then process the results of your PLINQ query using a Parallel.ForEach loop, the fluent interface style must stop.

Instead of using the Parallel class, you can elect to call the ForAll method, which is defined in ParallelQuery<T>. You provide the method with an Action delegate, which is executed once for each element in the query's results. In some cases, the in-line code produced using ForAll can be more elegant than that of a separate loop.

The previous example can be recreated using ForAll with the following code:

var values = Enumerable.Range(1, 50).Where(i => i % 2 == 0);
values.AsParallel().ForAll(i => Console.WriteLine(i));

This can be shortened into a single line of code, as follows:

Enumerable.Range(1, 50).AsParallel().Where(i => i % 2 == 0).ForAll(i => Console.WriteLine(i));

Considerations

ForAll has the same limitations as the other parallel loops. The parallelism gives the greatest benefits when the code of the Action delegate can be executed independently. If the iterations of the loop update shared data, you must implement thread synchronisation to avoid bugs caused by race conditions. For example, you should add critical sections with the lock statement or use the methods of the Interlocked class when making such changes.

6 November 2013