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 For Loops for any Data Type

The standard parallel for loop is limited to working with integer values and only permits looping through an incrementing range, unlike its sequential equivalent. This article describes an alternative parallel for loop without these limitations.

Controlling the Loop Control Variable

The next limitation to overcome is that the loop can only work with incrementing values. We've improved it somewhat by allowing the difference between values to be set but this is not nearly as flexible as the sequential for loop. To improve it, we need to allow any action to be performed upon the loop control variable to generate the next value.

We can add this functionality by replacing the increment parameter with a Func delegate. This delegate will accept a decimal number, being the previous iteration's value, and return another decimal, which will be used during the next iteration.

This is quite a simple change, as shown below. Note how the function is passed into the sequence generator method and called in the final part of the for loop.

static void ParallelFor(
    decimal start, decimal end, Func<decimal, decimal> next, Action<decimal> function)
{
    var values = GenerateSequence(start, end, next);
    Parallel.ForEach(values, function);
}

static IEnumerable<decimal> GenerateSequence(
    decimal start, decimal end, Func<decimal, decimal> next)
{
    for (decimal d = start; d < end; d = next(d))
    {
        yield return d;
    }
}

To try out the function we can create a loop where the control variable starts at 0.001 and is multiplied by ten after each execution. Try running the code below in the Main method. Note the lambda expression that performs the multiplication. Again, you can see from the sample output that the iterations are processed in parallel.

ParallelFor(0.1M, 1000000000M, d => d * 10, d => { Console.WriteLine(d); });

/* OUTPUT
     
0.1
10000.0
100000.0
1000000.0
1.0
10.0
10000000.0
100000000.0
100.0
1000.0

*/

Using a Predicate to Determine Loop Completion

The final limitation of Parallel.For when compared to a standard loop is that the loop terminates when a pre-set value is reached. We can remove this limitation by replacing the end value with another delegate, this time a Predicate delegate that accepts a decimal and returns either true or false. The predicate is evaluated after each iteration. If it returns false the loop should exit.

The updated version of the method is as follows. You can see that the predicate is called by the sequence generator method. Once false, the sequence ends. The parallel loop will then exit after all scheduled iterations are complete.

static void ParallelFor(
    decimal start, Predicate<decimal> condition,
    Func<decimal, decimal> next, Action<decimal> function)
{
    var values = GenerateSequence(start, condition, next);
    Parallel.ForEach(values, function);
}

static IEnumerable<decimal> GenerateSequence(
    decimal start, Predicate<decimal> condition, Func<decimal, decimal> next)
{
    for (decimal d = start; condition(d); d = next(d))
    {
        yield return d;
    }
}

Let's try the new version with a silly example that multiples a value by eleven repeatedly. The predicate in this case stops the loop once the length of the next value would be ten or more digits.

ParallelFor(1, d => d.ToString().Length < 10, d => d * 11M, d =>
{
    Console.WriteLine(d);
});

/* OUTPUT
     
1
121
1331
11
14641
214358881
19487171
161051
1771561

*/
5 October 2013