Typically, I would simply iterate over a enumerable collection and run against the data in parallel using Parallel.For and Parallel.ForEach
It turns out that there is no support for this in the PFX API. Parallel.For does not support incrementing other than 1 and it does not support reverse iteration. Also, Enumerable is a static class – which means we can’t do extension methods on Enumerable. However, we can use a custom iterator to generate the sequence for us. By using a Func<int, int> allows us to use our familiar i+= syntax for the increment function. We can quickly create a very simple iterator that will help us out.
This is great, but it only works for integers. We have two options, create overloads for all types we want to generate sequences for, or write a generic version. I have chosen to implement this as a generic implementation, but it means we have a few issues. First, we need to be able to detect the terminating condition (i < max) but we can’t use the < operator. Second, we need to be able to increment the loop indexer by an arbitrary amount; we have to be able to take a numeric or a lambda expression to increment the target type, but C#s type system gets in the way. Third, we need to be able to apply the offset to the minimum without the use of the + operator (we don’t need - since an expression like 5 - 3 is really (+5) + (-3)).
In order to solve these problems, I have chosen to use expression trees to create delegates that I can invoke to do the work. This does slow our code down as we are invoking method calls on delegate pointers instead of being able to make simple comparisons on primitive types.
Now we can get to the fun part and use the code to generate sequences. If you were to write overloads for int, float, double, decimal, uint, ulong, BigInteger, etc, like the first version, the code would be up to +20x faster than generating the sequence using Enumerable. We incur a lot of overhead using the expression trees and delegates as we invoke method calls instead of simple IL comparisons. However, I think that this version is much more maintainable and much smaller. Another downside to the use of generics is that we can’t use C# 4.0 default parameters which really cut down on the number of overloads we had to track manually. Keep in mind that like Enumerable, we are leveraging deferred execution in order to generate the sequences.
With the foundation laid, we can now generate sequences without having to do a post processing filter. Below is an example showing a typical usage of LINQ grabbing the first N odd numbers.
We can now go forward and backward, skip, and filter when generating sequences with a wide variety of types. Given this power, we can process our sequences in parallel using Parallel.ForEach just as we would any other collection. Our original goal of using Parallel.For is not possible so far as I can tell. But leveraging the versatility and deferred execution provided by this method, we can do just about anything we want now.
A word of warning. Do not use i => i++ or i => i-- as your incrementing function as you are not returning the incremented value and you will most likely generate an infinite sequence and an OutOfMemoryException!
If we look back to our original set of problems to solve, we have taken care of them all and can now run our variable incrementing series using Parallel.ForEach. Any of the examples show above can be run in parallel, but I will give you a few more examples.
Now I give you the full implementation for the Sequence class.