# C++ Concurrency in Action ***Practical Multithreading*** ***Anthony Williams*** ## Note ## About the book ## Resources ## Words granularity ## Usage ## Names ## Brief Contents - **1. Hello, world of concurrency in C++!** - **2. Managing threads** - **3. Sharing data between threads** - **4. Synchronizing concurrent operations** - **5. The C++ memory model and operations on atomic types** - **6. Designing lock-based concurrent data structures** - **7. Designing lock-free concurrent data structures** - **8. Designing concurrent code** - **9. Advanced thread management** - **10. Testing and debugging multithreaded applications** ## Contents - **1. Hello, world of concurrency in C++!** - What is concurrency? - Concurrency in computer systems - Approaches to concurrency - Why use concurrency? - Using cocurrency for separation of concerns - Using concurrency for performance - When not to use concurrency - Concurrency and multithreading in C++ - History of multithreading in C++ - Concurrency support in the new standard - Efficiency in the C++ Thread Library - Platform-specific facilities - Getting started - Hello, Concurrent World - Summary - **2. Managing threads** - Basic thread management - Launching a thread - Waiting for a thread to complete - Waiting in exceptional circumstances - Running threads in the background - Passing arguments to a thread function - Transferring ownership of a thread - Choosing the number of threads at runtime - Identifying threads - Summary - **3. Sharing data between threads** - Problems with sharing data between threads - Race conditions - Avoiding problematic race conditions - Protecting shared data with mutexes - Using mutexes in C++ - Structuring code for protecting shared data - Spotting race conditions inherent in interfaces - Deadlock: the problem and a solution - Further guidelines for avoiding deadlock - Flexible locking with `std::unique_lock` - Transferring mutex ownership between scopes - Locking at an appropriate granularity - Alternative facilities for protecting shared data - Protecting shared data during initialization - Protecting rarely updated data structures - Recursive locking - Summary - **4. Synchronizing concurrent operations** - Waiting for an event or other condition - Waiting for a condition with condition variables - Building with a thread-safe queue with condition variables - Waiting for one-off events with features - Returning values from background tasks - Associating a task with a future - Making `(std::)promises` - Saving an exception for the future - Waiting for multiple threads - Waiting with a time limit - Clocks - Durations - Time points - Functions that accept timeouts - Using synchronization of operations to simplify code - Functional programming with futures - Synchronizing operations with message passing - Summary - **5. The C++ memory model and operations on atomic types** - Memory model basics - Objects andd memory locations - Objects, memory locations and concurrency - Modification orders - Atomic operations and types in C++ - The standard atomic types - Operations on `std::atomic_flag` - Operations on `std::atomic` - Operations on `std::atomic` : pointer arithmetic - Operations on standard atomic integral types - The `std::atomic<>` primary class template - Free functions for atomic operations - Summary - **6. Designing lock-based concurrent data structures** - What does it mean to design for concurrency? - Guidelines for designing data structures for concurrency - Lock-based concurrency data structures - A thread-safe stack using locks - A thread-safe queue using locks and condition variables - A thread-safe queue using fine-grained locks and condition variables - Designing more complex lock-based data structures - Writing a thread-safe lookup table using locks - Writing a thread-safe list using locks - Summary - **7. Designing lock-free concurrent data structures** - Definitions and consequences - Types of non-blocking data structures - Lock-free data structures - Wait-free data structures - The pros and cons of lock-free data structures - Examples of lock-free data structures - Writing a thread-safe stack without locks - Stopping those pesky leaks: managing memory in lock-free data structures - Detecting nodes that can't be reclaimed using hazard pointers - Detecting nodes in use with reference counting - Applying the memory model to the lock-free stack - Waiting for a thread-safe queue without locks - Guidelines for writing lock-free data structures - Guideline: use `std::memory_order_seq_cst` for prototyping - Guideline: use a lock-free memory reclamation scheme - Guideline: watch out for the ABA problem - Guideline: identify busy-wait loops and help the other thread - Summary - **8. Designing concurrent code** - Techniques for dividing work between threads - Dividing data between threads before processing begins - Dividing data recursively - Dividing data by task types - Factors affecting the performance of concurrent code - How many processors? - Data contention and cache ping-pong - False sharing - How close is your data? - Oversubscription and execessive task switching - Designing data structures for multithreaded performance - Dividing array elements for complex operations - Data access patterns in other data structures - Additional consideration when designing for concurrency - Exception safety in parallel algorithms - Scalability and Amdahl's law - Hiding latency with multiple threads - Improving responsiveness with concurrency - Design concurrent code in practice - A parallel implemenation of `std::for_each` - A parallel implemenation of `std::find` - A parallel implemenation of `std::partial_sum` - Summary - **9. Advanced thread management** - Thread pools - The simplest thread pool - Waiting for tasks submitted to a thread pool - Tasks that wait for other tasks - Avoiding contention on the work queue - Work stealing - Interrupting threads - Launching and interrupting another thread - Detecting that a thread has been interrupted - Interrupting a condition variable wait - Interrupting a wait on `std::condition_varaible_any` - Interrupting other blocking calls - Handling interruptions - Interrupting background tasks on application exit - Summary - **10. Testing and debugging multithreaded applications** - Types of concurrency-related bugs - Unwanted blocking - Race conditions - Techniques for locating concurrency-related bugs - Reviewing code to locate potential bugs - Locating concurrency-related bugs by testing - Designing for testability - Multithreaded testing techniques - Structuring multithreaded test code - Testing the performance of multithreaded code - Summary ## Preface