Synchronization Primitives
Synchronization Primitives
Coordinate threads with the right primitive instead of forcing every problem into a single mutex.
Synchronization Primitives
Coordinate threads with the right primitive instead of forcing every problem into a single mutex.
Before choosing a primitive, decide what you are protecting:
The primitive should match the coordination problem.
std::shared_mutexUse this when reads are frequent and writes are rare.
std::shared_mutex cache_mutex;
std::unordered_map<std::string, int> cache;
int lookup(std::string_view key) {
std::shared_lock lock(cache_mutex);
return cache.at(std::string{key});
}
void update(std::string key, int value) {
std::unique_lock lock(cache_mutex);
cache[std::move(key)] = value;
}
Use a semaphore when several tasks may proceed concurrently, but only up to a fixed limit.
std::counting_semaphore<8> db_slots(8);
This is often a better fit than a mutex when you are modeling capacity instead of exclusive ownership.
std::latch: one-time coordination, such as "all workers are initialized".std::barrier: repeated step-by-step synchronization across iterations.Condition variables remain the right tool when threads must sleep until a state predicate becomes true.
cv.wait(lock, [] { return ready; });