It’s been a while since my last review (though not because of being idle), so I’m happy to present yet another one. This will be about a book discussing a topic I’m particularly interested in, concurrency. Without further ado, here it is.
The title of the book tells everything the reader needs to know about the contents of the book: it deals with concurrency from a C++ point of view and presents how the various facilities can be used in action. Thus, if anybody wants a book on concurrency, then the good news is that s/he probably found a good one, as no prior knowledge is required about this topic. The bad news is, that if somebody expects to simply pick up a book about concurrency while not concerning hemself/hirself about the language used to present the ideas (as it may be the case with many other books on various topics), then there will be some disappointment. But what does this all mean? Let’s get into details:
First off, although no prior concurrency knowledge is required, a fair amount of C++ familiarity is, so one can’t just pick this book up and expect great benefits without it. Not only that, but also a deep interest in C++ (and concurrcy) is highly recommended, as it gets (extremely) highly technical after a certain point, so it’s also not necessarily for the casual reader. Just to reinforce this last statement, let me quote a praise from the book that kept popping into my mind as I was going through it: “Reading this made my brain hurt. But it’s a good hurt.” An it really is, but thanks to the author’s well thought writing, the hurt doesn’t really start until chapter 5, so the reader will get a comfortable amount of the proverbial calm before the storm. But after that, expect some serious stuff.
The calm part starts off really mildly in chapter 1 with an intro about concurrency, when and why it should (or shouldn’t) be used, what is multithreading, how all these are supported in C++ and so on. Chapter 2 then continues with exploring threads and their management, which is followed by the explanation of the essential threading building blocks in the next two chapters. The author never misses to include a proper amount of code samples to present how and when these blocks are to be used. There will be plenty of talk about all the necessary theoretical background like deadlocks, various forms of race conditions, code structuring and so on, while guidelines like resource management, programming style, proper locking granularity, exception safety, etc. will not be excluded either.
One very interesting (and to me somewhat unexpected) inclusion in these early chapters was a short, yet very concise introduction to the chrono library, as a basic understanding of it is actually necessary for some of the threading facilities. It wasn’t unexpected because it doesn’t belong there (it actually does), but because unfortunately not all authors bother explaining stuff that is not strictly the topic of their books, so I was positively surprised. To me, little things like these really raise my appreciation level, as not needing to interrupt my reading experience (and focus) just to look up something that probably should’ve been included anyway, is a definite plus. Furthermore, the author wasn’t scanty about providing an overlook about planned C++ threading facilities, as a significant part of chapter 4 delves precisely into this topic. One could ask why these not yet officially supported features are included in the first place, and I’d like to think because it gives the reader a rather clear vision where the language is headed in this regard.
Chapter 5 then quickly swirls into the promised storm. It starts the discussion with the C++ memory model and how it relates to concurrency and then dives deeper to present various forms of atomic operations and types. The really deep stuff comes at the end of the chapter, where memory ordering and various relationships are introduced. To be honest, I don’t think a single read of this chapter (especially the last section of it) will suffice to make it sink in. Chapters 6 and 7 then deal with the creation of lock-based and lock-free concurrent data structures by providing plenty of guidelines, pitfalls and code examples. If one had doubts about the difficulties of writing proper concurrent code before, then all those doubts will be dispelled in these chapters for sure. Despite their difficult nature however, they are still very useful even to those, who do not wish to write concurrent containers, as proper usage of the basic building blocks introduced in the first half of the book is clearly shown here in action. Speaking about action, the book also includes an entire chapter where all these building blocks are used to increase performance of some common algorithms (with all associated pitfalls, guidlines, etc.), so the author truly delivers on the ‘in action’ part of the title.
If it wasn’t enough already, advanced threading paradigms like thread pools and thread interruption will also be presented along with overall design guidelines for writing concurrent code. There will also be a small section on debugging and testing, mostly for the sake of completeness and probably to give the reader a softer outro after all the complicated stuff. Finally, the book also contains a rather large appendix section (about 1/3 of the total pages) which can serve as a reference to the modern features and concurrency part of C++.
So is there a problem with this book one might ask? I don’t think there is (significant ones anyway), as it excels at the thing it set out for. There are plenty of code examples, discussions, explanations, introductions and the choice and order of topics are excellent. And here I’d like to dwell a bit on the order, as some books in the programming field like to forward reference themselves (use concepts that will only be explained later in the book), but not this one. Everything is laid down properly in time as it should be. If I were pressed hard to find a problem though, then the only thing (not a great deal really) I would say is the somewhat legacy nature of the presented code. Don’t get me wrong, it is not ancient style, but there are cases where it could be upgraded for better readability, like trying to replace the bit obscure ‘typedef typename std::result_of
::type result_type' with an easier to understand 'using result_type = std::result_of_t '; or maybe using 'auto' instead of 'std::shared_ptr ' where 'std::make_shared ' is used on the right side of the statement anyway. As I have said, these are not significant problems and since the author expects good knowledge of C++, these will not be a showstopper.
All in all, this is an excellent and challenging book intended for those who are interested in and appreciate both C++ and concurrency. If you are new to concurrency, then this is definitely worth your time, but be warned: if you don’t have proper understanding of the language itself, you will then be struggling with it far more than necessary. I’d even go as far as saying that this book has prerequisite literature, like Stroustrup’s A Tour of C++ and Meyers’ Effective Modern C++ to actually have a chance of really learning from it. Finally, because of the challenging nature of this piece, don’t expect a single read to be enough, so prepare for putting in extra effort to your usual learning methods, like creating diagrams and mental images of the execution sequences to fully benefit from it.