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_lockTransferring 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::)promisesSaving 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_flagOperations on
std::atomic<bool>Operations on
std::atomic<T*>: pointer arithmeticOperations on standard atomic integral types
The
std::atomic<>primary class templateFree 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_cstfor prototypingGuideline: 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_eachA parallel implemenation of
std::findA 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_anyInterrupting 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