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+

Identifying Collection Indexes in Parallel ForEach Loops

When using a sequential foreach loop it is simple to include a counter variable to determine the index of the item that is being operated upon. With parallel foreach loops the index must be identified in a different manner.

Parallel.ForEach Item Indexes

When you are processing a collection or an array of values within a foreach loop, it is a simple task to determine the index of the item being worked with during the current iteration. You can declare a variable outside of the loop and initialise it with the first index value, which is usually zero. At the end of each iteration you can increment the value so that it tracks the index being processed.

For example, the following code includes a foreach loop that calculates sixteen powers of two, outputting each value and its index to the console. The results are shown in the comment.

var values = Enumerable.Range(0, 16).Select(v => (int)Math.Pow(2, v));

int index = 0;
foreach (int value in values)
{
    Console.WriteLine("{0}:\t{1}", index, value);
    index++;
}

/* OUTPUT

0:      1
1:      2
2:      4
3:      8
4:      16
5:      32
6:      64
7:      128
8:      256
9:      512
10:     1024
11:     2048
12:     4096
13:     8192
14:     16384
15:     32768

*/

When using a parallel foreach loop the above approach will not work. As the numbers may be processed in a different order to the sequential version, the index values in a counter variable would likely be different to that of the items as they are processed. In addition, you would need to be careful when updating the counter to ensure that a race condition did not exist that caused two values to use the same index.

Luckily, the Parallel.ForEach method includes several overloaded versions that allow the item indexes to be identified within the iterations. All you need to do is use an Action delegate that includes three parameters. The first will be set to the value to be processed during each iteration. The second is the ParallelLoopState object that allows you to terminate the loop if desired. The third parameter is the one we are interested in. It is set to a 64-bit integer, or long, containing the index of the item being processed.

To see how this works, consider the sample code below. Here the Action delegate for the loop has three parameters named, "value", "pls" and "index". value will be set to the value being processed, pls is the loop state object and index will be set to the index of the item for each iteration. The output from the method shows that the indexes are correctly matched to the results, although they appear in a different order than the sequential sample seen earlier.

var values = Enumerable.Range(0, 16).Select(v => (int)Math.Pow(2, v));

Parallel.ForEach(values, (value, pls, index) =>
{
    Console.WriteLine("{0}:\t{1}", index, value);
    index++;
});

/* OUTPUT

0:      1
3:      8
1:      2
2:      4
4:      16
9:      512
10:     1024
11:     2048
12:     4096
13:     8192
14:     16384
6:      64
7:      128
8:      256
5:      32
15:     32768

*/
30 September 2013