November 29: Soma has announced a CTP release of the Parallel Extensions to the .NET framework, available to download as a full product (approx. 1.7MB) or just CHM documentation. For more info, look at the Parallel Programming blog and the Parallel Computing developer center on MSDN.
Software consultants, architects, decision-makers and community leaders have been blowing the “free lunch is over” horn for several years now. With the advent of multi-core machines, Moore’s law has been rephrased: the individual core’s speed is not dramatically increasing, but the number of cores is expected to double every X months. As Herb Sutter says in very simple terms:
“Concurrency is the next major revolution in how we write software.”
Curiously, I’ve noticed a completely different but related phenomenon a few years ago, when I started teaching: there are three concepts to grasp on your way to becoming a decent developer. The first concept is recursion, which is a great filter but probably most CS students can make ends with it. The second concept is pointers (and the underlying memory model), which seems to become more and more extinct in modern programmers, with the high-level abstractions we have today. Finally, the third concept is parallelism, concurrency and multi-threading – and while there are countless programmers I know who can recite a recursive Fibonacci algorithm, there is perhaps only half a dozen who can wisely opinionate on concurrency-related issues.
The advent of libraries such as the Parallel Extensions for .NET marks a very distinctive point in the evolution of developers: concurrency, parallelism, multi-threaded algorithms must enter our arsenal if we want to stay in business. It’s as simple as that. Or at least that’s what I like to hope for.
So what’s in the Parallel Extensions CTP for you? It has been summarized in several places, and there’s an extensive CHM documentation file distributed with the download, but I figured that a few brief and concise examples will drive the point home even better. Everything demonstrated here comes from the System.Threading.dll assembly, from the CTP release.
Parallel LINQ (PLINQ)
With PLINQ, it’s now possible to execute any (well, almost any) LINQ query in parallel. This is achieved by using the ParallelEnumerable<T> static class with extensions methods mimicking those of LINQ’s Enumerable<T>. For example, the following query to select prime numbers from a range is executed in parallel:
IEnumerable<int> range = …; // obtain range from somewhere
var primeNumbers = from n in range.AsParallel()
where n => IsPrime(n)
For more information on PLINQ, consult the CTP documentation or take a look at this PLINQ October 2007 MSDN Magazine article.
If you’re not after parallelizing data access, but rather you have some loops or tasks to perform concurrently, you have the high-level Parallel static class at your service. For example, to parallelize a loop, you could write:
for (int i = 0; i < 10000; ++i) ComputeSomething(i); // single-threaded version
Parallel.For(0, 10000, i => ComputeSomething(i)); // parallelized version
This will not create 10,000 threads running your work in parallel; the parallel library determines the reasonable level of concurrency itself.
For more information on the Parallel Library, take a look at this Parallel FX October 2007 MSDN Magazine article.
Creating tasks is similar to creating thread pool work items, but a task provides you a higher level of control. For example, you can wait for tasks to complete:
Task t = Task.Create(() => ComputeSomething());
//… do some work
t.Wait(); // wait for the task to complete
Or you can cancel tasks in the middle without resorting to manual synchronization or ugly aborts:
Task t = Task.Create(() =>
//… do some work
t.Cancel(); // does not Thread.Abort, but sets IsCanceled property
Futures are tasks with an associated value. Normally, there would be a future process to calculate the value. For example:
Future<int> fi = Future.Create(() => ComputeNumber());
//… at a later point:
Console.WriteLine(fi.Value); // waits if necessary
Alternatively, you can create a future without an associated method (delegate or lambda), in which case you have to set its value manually:
Future<string> fs = Future.Create<string>(); // no delegate
// main thread:
Console.WriteLine(fs.Value); // waits
// other thread:
fs.Value = “Computed String”; // releases main thread
Hopefully, this short tour around the Parallel Extensions library was useful for you. If these concepts seem new and appealing – go ahead and grab the CTP right now to play with the bits. There is a change, be part of it. If you think this has nothing to do with the way you are developing software – think again. You are getting surrounded.