Developer

Code concepts: Parallel extensions

The Parallel Extensions library from .NET (PFx) supports a range of parallel programming paradigms. Here's an overview of the three main concepts in PFx.

 

I've written about the Parallel Extensions library from .NET (PFx) a few times; the last time I looked at PFx, it was a Community Technology Preview available for .NET 3.5. At the time, it was poorly documented, and while it was possible to muddle through the "how" of using its various parts, it was tough to see the "why" behind some of them. As .NET 4 gets closer, details on PFx have gotten clearer, and now that .NET 4 is in Beta 2, I think it's a good time to take another look at PFx.

PFx is divided into three main concepts: imperative parallelism, tasks, and declarative parallelism.

Imperative parallelism

The imperative parallelism will feel the most familiar and comfortable to the majority of developers. The basis is the Parallel static class, which contains three important static methods: For(), ForEach(), and Invoke().

With For(), you pass it a beginning number, an end number, and a delegate that accepts a single input parameter of type Int32; then, it acts as though it was iterating from the beginning number to the end number and passing the iteration's index to the delegate. Unlike a normal for loop, though, it can execute multiple iterations at the same time, and there is no guarantee that they will be executed in any particular order.

ForEach() accepts a collection that implements IEnumerable and a delegate that accepts an input parameter of the type that the collection's enumerator outputs. It enumerates over the collection, passing each item to the delegate. Like For(), it can process each iteration in parallel and has no guarantee of execution order.

With Invoke(), you pass any number of delegates to it, and it executes them as a batch, in no particular order and in parallel if possible.

All of these methods are easy to use and can be bolted onto existing code fairly painlessly. In the case of For() and ForEach(), you just take the bodies of existing for and foreach statements, wrap them into a lambda, and pass the lambda to the static method call, after adding any necessary data integrity code. Likewise, you can take an existing group of statements, turn them into lambdas, and pass them to Invoke() without too much effort.

From the demos I have given and the numerous presentations I have attended, the imperative parallelism provokes the most favorable reaction from the crowd because it most closely resembles how most developers already write code.

Tasks

Tasks were poorly explained in the early days of PFx, but the documentation has improved, and they are much easier to understand.

Tasks represent a unit of work, like a Thread does. Unlike Thread, Tasks have a ton of useful built-in functionality, including easily getting input in and out and cancellation and coordination mechanisms.

There is a lot of in-depth functionality within Tasks, far too much to cover here. If you need fine-grained control over your code's execution, or your different pieces of parallel code need to work closely with one another, Tasks are where it is at.

Declarative parallelism

If you're using LINQ, you'll be pleased to know that PFx includes Parallel LINQ (PLINQ). PLINQ extends LINQ by allowing it to work in parallel when possible and when it will provide performance gains. There is a complicated formula to determining if a PLINQ query will run in parallel, but let it suffice to say at this time that some LINQ operators will always cause the statement to run sequentially, and the more complex your query is, the less likely it is to run in parallel.

To use PLINQ, simply run your LINQ queries against the AsParallel() method of an IEnumerable instead of against the object itself. For example, instead of:

from item in items where item.value > 5 select item.name

you would write:

from item in items.AsParallel() where item.value > 5 select item.name

That's pretty easy as far as performance gains go.

Conclusion

PFx supports a range of parallel programming paradigms to allow you to work with one that most closely matches your style and development needs. I suggest that you start with the imperative parallelism and the declarative parallelism (if you're already using LINQ) as a way of quickly getting your feet wet without much investment of time or learning, and then get familiar with Tasks if you like what you see.

J.Ja

Disclosure of Justin's industry affiliations: Justin James has a contract with Spiceworks to write product buying guides; he has a contract with OpenAmplify, which is owned by Hapax, to write a series of blogs, tutorials, and articles; and he has a contract with OutSystems to write articles, sample code, etc.

———————————————————————————————————————————-

Get weekly development tips in your inbox Keep your developer skills sharp by signing up for TechRepublic's free Web Developer newsletter, delivered each Tuesday. Automatically subscribe today!

About Justin James

Justin James is the Lead Architect for Conigent.

Editor's Picks

Free Newsletters, In your Inbox