Parallel Programming with Microsoft .NET:
Design Patterns for Decomposition and Coordination on Multicore Architectures by Colin Campbell, Ralph Johnson, Ade Miller and Stephen Toub
Publisher: Microsoft Press
Over the last years, trends and developments in hardware architectures have driven software increasingly into parallelism. With Java's concurrency libraries and the parallel extensions to .NET 4, parallel programming is indeed entering the mainstream. But adding language and library support on top of these imperative languages, designed with a sequential programming model in mind, is challenging to say the least. Given that constraint, the .NET extensions are quite impressing. And Parallel Programming with Microsoft .NET is a high-quality book that makes an excellent job in capturing idiomatic usages in C#. Even if I'm in favor of this book, the technology it presents worries me. Join me, and I'll explain before returning to the parallel patterns themselves.
My main concern with all solutions that add support for parallelism as an after-thought is that they usually involve an absurd amount of analysis to get absolutely right. And the resulting complexity is often overwhelming. Worse, these traditional techniques require us to write our programs differently for multicores than what we would have done on a single core chip; the programmer has to make an active choice to introduce parallelism. Thus, it's a pretty sure bet that such solutions simply won't scale on hundreds or thousands of processors. What we need is technologies that don't differentiate between single and multiple cores with respect to the coding constructs. That is, technologies that allow the natural parallelism of the problem to be expressed. Based on such technologies, our programs would be able to automatically scale depending on available hardware resources. In fact, this is exactly how Erlang works. And even if .NET is far away from Erlang's elegance and simplicity, the patterns presented in this book are a step in the right direction.
What attracts me about the included patterns is the philosophy behind them; the authors tend to prefer lock-free and scalable constructions. The idea is to declare the potential parallelism of the program and leave it to the run-time system to realize it. Not only is this a step in the desirable direction I discussed above; it's also a true paradigm-shift for imperative programming languages leaving threads and locks behind. The patterns themselves range from rather every-day constructs like parallel loops, parallel aggregations and futures to high-level patterns. An example from the latter category is the Pipeline Pattern. A Pipeline is basically a codification of Unix pipes, where each task depends on the output of its predecessor. The different tasks in the pipeline are connected by use of blocking queues.
It is in these more complex patterns that the quality of this work shines through. The authors include interesting discussions of common variations, anti-patterns and notes for the detailed design. By including hints and key-points in the margin, the patterns are easy to navigate and skim without much risk of missing a significant point. In fact, the book is exemplary in many aspects. The writing itself is fluent, motivating and written by people who know their subject inside out. The accompanying discussions all add value with a minimum of noise. And the code samples are solid and complete. This book is simply excellent. It's a high-quality work that I would recommend to any developer facing the challenges of parallel programming in a mainstream object-oriented language. But make sure to check-out a radically different approach to parallelism too. Your programs will love you for it.
Reviewed March 2011