c++17起,std::shared_mutex是标准库唯一原生读写锁,支持多读单写;应搭配std::shared_lock(读)和std::unique_lock(写),禁用嵌套加锁,注意生命周期与平台支持。

std::shared_mutex 在 C++17 中怎么用
直接说结论:C++17 起,std::shared_mutex 是标准库唯一原生支持读写锁的类型,它允许多个线程同时读、但写独占。别再手写 std::mutex + 计数器模拟——既易错又不保证原子性。
常见错误是把 std::shared_mutex 和 std::shared_timed_mutex 混用:std::shared_timed_mutex 虽然也支持共享/独占语义,但性能更差(内部有额外时序逻辑),且在 C++20 中已被弃用。除非你卡在 C++14 或更低版本,否则一律选 std::shared_mutex。
-
std::shared_lock<:shared_mutex></:shared_mutex>用于只读场景,构造即加共享锁,析构自动释放 -
std::unique_lock<:shared_mutex></:shared_mutex>或std::lock_guard<:shared_mutex></:shared_mutex>用于写操作,获得独占访问权 - 不能混用
std::shared_lock和std::unique_lock对同一个std::shared_mutex实例做嵌套加锁(比如读锁里再尝试写锁),会死锁
示例片段:
std::shared_mutex rw_mutex;
std::vector<int> data;
<p>void read_data() {
std::shared_lock<std::shared_mutex> lock(rw_mutex); // 共享读
for (int x : data) { /<em> ... </em>/ }
}</p><p>void write_data(int x) {
std::unique_lock<std::shared_mutex> lock(rw_mutex); // 独占写
data.push_back(x);
}</p>为什么 shared_lock 构造失败时可能抛 exception
std::shared_lock 默认构造不加锁,但带 std::defer_lock、std::try_to_lock 或 std::adopt_lock 参数时行为不同;真正容易出错的是 std::try_to_lock 模式下,如果锁不可获取,它不会阻塞,而是设内部状态为未锁定 —— 这本身不抛异常。真正抛 std::system_error 的情况只有一种:std::shared_lock 尝试对一个**已损坏或被销毁的 std::shared_mutex** 加锁。
立即学习“C++免费学习笔记(深入)”;
- 典型坑:把
std::shared_mutex成员变量声明为局部变量,或在多线程环境下提前析构了锁对象 - 另一个隐形坑:在 signal handler 里调用
shared_lock——std::shared_mutex不是 async-signal-safe,会导致未定义行为 - 调试建议:开启 ASan + TSan 编译,能快速暴露生命周期问题;不要依赖
try_lock()返回值做业务逻辑分支,它只是“此刻是否抢到”,不代表后续安全
Windows 上编译报错 “shared_mutex not declared in this scope” 怎么办
不是你代码写错了,是编译器或标准库没跟上。MSVC 从 19.29(VS 2019 v16.11)才完整支持 std::shared_mutex;MinGW-w64 则依赖 libstdc++ 版本,GCC 9.1+ 才稳定可用。Clang on Windows 若用 MSVC STL,同样受制于 VS 版本。
- 检查方式:编译时加
-dM -E看宏定义,确认__cpp_lib_shared_mutex是否 ≥ 201505L - 替代方案:若无法升级工具链,用
SRWLOCK(Windows API)+AcquireSRWLockShared/AcquireSRWLockExclusive,轻量且无分配开销,但需手动管理生命周期 - 跨平台妥协:封装一层
rw_mutex接口,在 Windows 下桥接到SRWLOCK,Linux/macOS 下用std::shared_mutex,避免业务代码到处 #ifdef
读写锁比互斥锁慢?什么时候不该用 shared_mutex
是的,std::shared_mutex 内部状态更复杂,加锁/解锁开销比 std::mutex 高 2–5 倍(实测 clang 15 / Linux x86-64)。它只在「读远多于写」且「读操作本身较重」时才有收益。
- 别用在高频短读场景:比如每微秒都查一次计数器,用
std::atomic<int></int>更合适 - 别用在写占比 >15% 的场景:此时共享锁带来的调度竞争反而拖慢整体吞吐
- 注意 false sharing:如果
std::shared_mutex和被保护的数据紧挨着放在同一 cache line,读线程频繁访问锁会把整行 invalid,导致数据缓存失效 —— 可用alignas(64)隔离锁变量
最常被忽略的一点:std::shared_mutex 不提供 writer-preference 或 reader-preference 保证,标准只规定“公平性未指定”。实际中,Linux glibc 实现偏向写者(避免写饥饿),而某些嵌入式 STL 可能完全相反。如果你的业务逻辑隐含了“写必须及时生效”的假设,得自己加超时或轮询兜底。










