Concurrency
Mutexes
Synchronize shared resources using std::mutex, recursive_mutex, shared_mutex, and RAII scope locks.
Interview: Preventing data races, comparing unique_lock vs lock_guard, and using shared_mutex for reader-writer optimizations.
A Mutex (mutual exclusion) is used to prevent data races by ensuring that only one thread can access a shared resource at a time. In C++, you should always manage mutexes using RAII wrappers to avoid leaving locks acquired.
std::lock_guard
A basic scope-lock wrapper. It locks the mutex on construction and unlocks it automatically on destruction.
std::unique_lock
An advanced lock supporting deferred locking, manual unlocking, and integration with condition variables.
std::shared_mutex
Introduced in C++17. A reader-writer lock allowing multiple concurrent readers or a single writer.
Reader-Writer Locks (std::shared_mutex)
When a shared resource is read frequently but modified rarely, standard exclusive mutexes limit throughput. std::shared_mutex resolves this:
- Reader Path: Locks using
std::shared_lock. Multiple threads can read concurrently. - Writer Path: Locks using
std::unique_lock. Blocks all other readers and writers to perform updates safely.
Code Walkthrough
A thread-safe cache class illustrating the use of reader-writer locks.
#include <iostream> #include <shared_mutex> #include <unordered_map> #include <string>class SafeCache { private: mutable std::shared_mutex m_mutex; std::unordered_map<std::string, std::string> m_map;
public: // Shared Lock for reading: multiple threads can read at once std::string get(const std::string& key) const { std::shared_lock<std::shared_mutex> lock(m_mutex); auto it = m_map.find(key); return (it != m_map.end()) ? it->second : ""; }
// Unique Lock for writing: blocks all other reads and writes void set(const std::string& key, const std::string& val) { std::unique_lock<std::shared_mutex> lock(m_mutex); m_map[key] = val; } };
int main() { SafeCache cache; cache.set("C++", "Awesome"); std::cout << "C++: " << cache.get("C++") << std::endl; return 0; }
Interview-Relevant Information
Q: What is the difference between std::lock_guard and std::unique_lock?
Answer: std::lock_guard is a lightweight, non-copyable RAII scope-lock. It lacks features to manual unlock or defer locking. std::unique_lock supports advanced operations, such as manual unlock(), deferred locking, and moving lock ownership, but has a slightly higher memory footprint due to tracking the lock state flag.
Q: When does std::recursive_mutex make sense, and what is its drawback?
Answer: std::recursive_mutex allows the same thread to acquire the lock multiple times without deadlocking. It is useful in recursive functions or when nested member functions call each other. However, it is slower than a standard mutex and often points to design flaws where lock scopes could be separated.
Quick Checklist
Did you avoid calling lock() manually? Do you use shared locks for read-only access? If yes, your resource synchronization is efficient.
Use Cases
Synchronizing shared memory structures (caches, log files, hash tables) in server environments.
Protecting database session collections from simultaneous modification.
Common Mistakes
Manually managing locks using raw .lock() and .unlock(), which causes deadlocks if an exception is thrown before unlocking.
Locking a standard std::mutex twice on the same thread, causing a self-deadlock.