Book review: C++ Concurrency in Action: Practical Multithreading

In the new C++ standard, multithread finally appears, with the old standard supported with TR2. This new addition has numerous implications on how programs are coded, and there are of course almost no book on this matter. This one is an exception.

Note: this review is not based on the final version that is now available (June the 28th), but on the MEAP one. There may be some differences between the final draft and the one I based my review on, although I don’t expect many, and certainly not any huge change.

Content and opinions

The book consists of ten chapters and mainly two center/example chapters.

The book starts with an introduction on concurrency, its benefits and its drawbacks. Of course, as multithreading was only integrated in C++0x, the introduction also mentions what you need to benefit from reading the book.

The second chapter starts the actual crash course with the introduction of threads and the interaction with them (joining them, counting their optimal number, …). Then, the important issue of data is addressed through the introduction of mutexes and locks. Each time, an example is provided with a detailed explanation of what is going on. The explanation here is enough and you don’t need at this point to write and test yourself, although you should nonetheless try and test the examples.

The next increment tackles the subject of synchronization. The new C++ objects make it easy to simply wait for a condition or to use futures, a nice way of handling results not yet available but that are being computed in another thread. The different approaches are well explained wit usefull examples, and althought he different solutions are complicated, the book makes them affordable. Then comes the difficult subject of atomic operations and of the new memory model. Ehere the explanations lack an introduction on why there are some operations that may not be seen from another thread right away, and why others are. The cache behavior on modern PCs makes me think that there may be some code that works well on them with the simpliest memory operations, and that won’t work on computers that don’t have cache coherency. Even with all the examples, this chapter is hard to understand, and you may need to write and draw everything to figure out what may happen, because it is certainly not easy.

After this hard-to-follow-even-with-images chapter, the author offers 2 example chapters. The first example encompasses several lock-based containers (a stack, a queue, …), and the author uses increasingly more of the tools from the first chapters to build this thread-safe container. There are several pitfalls that one has to check, and the book seems to counter each of them. There is of course several pages of corresponding code, and it takes a lot of time to figure out everything (although you have the explanation just after each code snippet), but this is mandatory. Then, the second set of examples focuses on lock-free containers. these are very hard to create, even with atomic types, and I’ll always remember Herb Sutter’s articles on this subject. The solution to this issue is very complicated, with references to hazard pointers, reference counting, … And in the end, if the first example chapter gave a lock-based list, the most complicated container in the second chapter is a lock-free queue, and both seem to have a similar line number.

Once these examples are over, the last three chapters are helpers to design correct multithreaded code. They don’t cover multithreaded C++, but how a serial program may turn into a multithreaded one. The first of these chapters covers the two different ways of parallelizing (data or task parallel), the different factors to consider (number of thread, cache behavior, …) and finally some examples of “simple” STL functions that can be parallelized (for_each, find, partial_sum). The second chapter covers the implementation of data and task parallelism with thread pools, work queues, task stealing, interruption, exception safety, … Finally, the last chapter handles debugging and the different things to test in a multithreaded code. It may be the least covered topic although it is one of the main pitfalls of multithreading. Unfortunately, there is no debug tool list, so you have to rely on other sources.

Conclusion

I don’t know if it is possible to make the book simpler, as these topics are complicated. This balanced by the fact that there are examples that help understand what is going on and what needs to be checked at all times. Mastering multithreading is a task accomplished only by practice, but this book makes the learning curve easier.

Leave a Reply