Working with Shared Data
Will be covering
- Basic mutex class (
std::mutex) - Mutex wrappers (
std::lock_guardandstd::unique_lock) - Mutexes and time-outs (
std::timed_mutex,std::unique_lock) - Shared mutexes (
std::shared_mutexandstd::shared_lock) - Static and thread-local data
- Deadlock
- Deadlock avoidance (
std::scoped_lock,std::lock()) - Livelock and Livelock avoidance
Locking Guidelines
-
Locking impacts on other threads
- They will have to wait longer for a resource they need
- This affects performance
-
Always hold a lock for the shortest possible time
-
Avoid locking lengthy operations if possible
- e.g input/output
Recommendations for Reading Shared Data
- Reading
- Lock
- Make a copy of the shared data
- Unlock and process the copy
Recommendations for Writing Shared Data
- Writing
- Lock
- Make a copy of the shared data
- Unlock and process the copy
- Lock again
- Update the shared data from the copy
- Unlock
Locking Guidelines for Data Structures
-
Do not lock any more elements than necessary
- e.g. Accessing a single element in a linked list
- Do not lock the entire list
- This will block other threads from accessing unrelated elements
-
Do not make locking too fine-grained
- Do not lock individual elements when inserting and deleting
- Another thread may need to access a neighbouring element
- Data race
- Need to lock neighbouring elements in a single operation
Pros and Cons of Mutexes
-
Fairly straightforward way to protect shared data
- Prevent data races and race conditions
-
Locking and unlocking are slow operations
-
Low level
- The programmer must remember to use a mutex
- The programmer must use the right mutex
- The programmer must understand how different threads can modify the data
-
Most real world programs use higher level structures
- Mutex wrapper class
- Classes from the next section