C++
shared_mutex
multithreading
concurrency
implementation

C shared_mutex implementation

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

std::shared_mutex is a reader-writer lock in C++ that allows many readers or one writer. It is useful when reads are frequent, writes are rare, and a plain std::mutex would force unnecessary serialization.

The name in this article says "C shared_mutex," but the standard type is a C++ feature from C++17. If your compiler or standard library does not provide it, the practical alternatives are std::shared_timed_mutex in C++14 or a platform primitive such as pthread_rwlock_t.

What std::shared_mutex Does

A normal mutex has one mode: locked or unlocked. A shared mutex has two lock modes:

  • shared mode for readers
  • exclusive mode for writers

Multiple threads can hold the shared lock at the same time, but once a writer takes the exclusive lock, no other reader or writer may enter.

This pattern works well for caches, configuration snapshots, and lookup tables that are read far more often than they are modified.

Basic Usage in C++17

You need the header shared_mutex and either std::shared_lock for readers or std::unique_lock for writers.

cpp
1#include <iostream>
2#include <mutex>
3#include <shared_mutex>
4#include <string>
5#include <thread>
6#include <unordered_map>
7#include <vector>
8
9class Cache {
10public:
11    void put(const std::string& key, int value) {
12        std::unique_lock lock(mutex_);
13        data_[key] = value;
14    }
15
16    bool get(const std::string& key, int& value) const {
17        std::shared_lock lock(mutex_);
18        auto it = data_.find(key);
19        if (it == data_.end()) {
20            return false;
21        }
22        value = it->second;
23        return true;
24    }
25
26private:
27    mutable std::shared_mutex mutex_;
28    std::unordered_map<std::string, int> data_;
29};
30
31int main() {
32    Cache cache;
33    cache.put("apples", 10);
34
35    int value = 0;
36    if (cache.get("apples", value)) {
37        std::cout << value << '\n';
38    }
39}

The important detail is that readers use std::shared_lock, not std::lock_guard. A write operation must use exclusive locking because it mutates shared state.

When It Helps and When It Does Not

std::shared_mutex is not automatically faster than std::mutex. It helps only when:

  • the protected data is read much more often than written
  • read sections are not extremely short
  • contention is high enough that reader parallelism matters

If writes are frequent, the shared mutex can be slower because the implementation must manage two lock modes and fairness rules. For tiny critical sections with little contention, a plain mutex is often simpler and just as fast.

If std::shared_mutex Is Missing

Some toolchains support C++14 but not full C++17. In that case, std::shared_timed_mutex is a common replacement:

cpp
1#include <iostream>
2#include <mutex>
3#include <shared_mutex>
4#include <thread>
5
6std::shared_timed_mutex rwlock;
7int shared_value = 0;
8
9void reader() {
10    std::shared_lock<std::shared_timed_mutex> lock(rwlock);
11    std::cout << shared_value << '\n';
12}
13
14void writer() {
15    std::unique_lock<std::shared_timed_mutex> lock(rwlock);
16    ++shared_value;
17}

If you are actually writing C rather than C++, POSIX provides pthread_rwlock_t, which solves the same reader-writer problem at the OS API level.

Implementation Notes

At a high level, a shared mutex implementation tracks:

  • whether an exclusive owner exists
  • how many shared owners exist
  • which waiters are blocked for shared or exclusive access

Real implementations also need policies for fairness. If readers are allowed to stream in forever, a waiting writer can starve. If writers are given strong preference, readers can stall badly under moderate write load.

That is why rolling your own reader-writer lock is usually a bad trade unless you have very specific requirements. The standard library or platform runtime has already dealt with memory ordering, wake-up behavior, and starvation tradeoffs.

Compile Example

Compile with C++17 support and threading enabled:

bash
g++ -std=c++17 -pthread shared_mutex_demo.cpp -o shared_mutex_demo

If your compiler reports that std::shared_mutex is missing, try:

bash
g++ -std=c++14 -pthread shared_mutex_demo.cpp -o shared_mutex_demo

and switch the code to std::shared_timed_mutex.

Common Pitfalls

The most common mistake is using the wrong lock type. Readers need std::shared_lock, while writers need std::unique_lock or another exclusive lock wrapper. If you use exclusive locks everywhere, you lose the benefit of the shared mutex.

Another mistake is assuming it belongs to C rather than C++. Standard shared_mutex is not a C language feature. In C, use a platform reader-writer lock such as pthread_rwlock_t.

Developers also overestimate the performance win. Measure before and after. If your workload is write-heavy or the critical section is tiny, std::mutex may be the better choice.

Finally, do not read shared state outside the lock and then act on it later as though it were still valid. A shared mutex protects data only for the lifetime of the lock object.

Summary

  • 'std::shared_mutex is a C++ reader-writer lock, not a standard C feature.'
  • It allows many readers or one writer at a time.
  • Use std::shared_lock for reads and std::unique_lock for writes.
  • It is most useful for read-heavy workloads with meaningful contention.
  • If unavailable, std::shared_timed_mutex or pthread_rwlock_t are practical fallbacks.
  • Do not assume it is faster than std::mutex without measuring your workload.

Course illustration
Course illustration

All Rights Reserved.