C++中std::shared_mutex怎么实现读写锁_C++17并发编程优化【同步】

1次阅读

std::shared_mutex是c++17读写锁,依赖os原生支持(pthread_rwlock_t或srwlock),需启用_cxx17且平台满足要求;必须配合shared_lock(读)或unique_lock(写)使用,不支持lock_guard;存在写饥饿、无写优先策略、跨平台兼容性差等问题,仅在读多写少且临界区长时具优势。

C++中std::shared_mutex怎么实现读写锁_C++17并发编程优化【同步】

std::shared_mutex 是 C++17 引入的读写锁,但不能直接用于 windows XP 或旧 MSVC 工具集

它在 linux(glibc ≥ 2.26)和较新 Windows(visual studio 2015 Update 3 起,需 _HAS_CXX17 启用)上可用,但底层依赖 OS 原生支持:pthread_rwlock_t(Linux)或 SRWLOCK(Windows Vista+)。若目标平台不满足,编译会失败或退化为互斥模拟——此时性能反而更差。

  • 检查是否启用 C++17:MSVC 需定义 _HAS_CXX17=1,Clang/GCC 需 -std=c++17
  • Windows 上若链接到旧 CRT(如 /MDd 对应 debug 版旧库),std::shared_mutex 构造可能抛出 std::system_error,错误码为 not_supported
  • 不要假设它“一定比 std::mutex 快”——读多写少且临界区长时才体现优势;写操作仍会阻塞所有读,且内部有额外原子开销

正确使用 shared_lock 和 unique_lock 配合 shared_mutex

std::shared_mutex 本身不提供 lock/unlock 接口,必须搭配 std::shared_lock(共享)或 std::unique_lock(独占)使用。混用或漏写会导致未定义行为,比如用 std::lock_guard 包裹 std::shared_mutex 会编译失败。

  • 读操作:用 std::shared_lock<:shared_mutex></:shared_mutex>,支持延迟构造、可移动、可 try_lock_for
  • 写操作:只能用 std::unique_lock<:shared_mutex></:shared_mutex>std::lock_guard 不支持(无 lock() 成员)
  • 禁止跨作用域传递锁对象shared_lock 移动后原对象变为未持有状态,再次 unlock 会抛异常
std::shared_mutex rwmtx; std::vector<int> data; <p>// 读 void read() { std::shared_lock<std::shared_mutex> lock(rwmtx); // 自动 RAII for (int x : data) { /<em> ... </em>/ } }</p><p>// 写 void write(int x) { std::unique_lock<std::shared_mutex> lock(rwmtx); data.push_back(x); }

shared_mutex 的饥饿问题与写优先策略缺失

std::shared_mutex 标准未规定调度策略,实际实现多为“读优先”,即持续有读请求时,写线程可能无限等待(写饥饿)。它也不提供类似 std::shared_timed_mutextry_lock_shared_for 之外的超时写锁接口,无法主动破除饥饿。

  • 无法通过标准接口设置写优先;若业务要求强一致性(如配置热更新),需自行加计数器或信号量控制写请求准入
  • 避免在循环中反复获取 shared_lock,尤其配合 long-running 读操作——这会显著延长写等待时间
  • 调试时注意:GDB/Lldb 可能无法准确显示 shared_mutex 当前持有者数量,需靠日志或自定义 wrapper 统计

替代方案:何时该放弃 shared_mutex 改用其他同步机制

当出现频繁写、数据结构简单、或需跨平台兼容旧系统时,std::shared_mutex 往往不是最优解。它的优势只在明确的“高并发读 + 低频写 + 大临界区”场景下成立。

立即学习C++免费学习笔记(深入)”;

  • 写占比 > 15%:直接用 std::mutex 更轻量,避免共享锁的原子计数开销
  • 需要乐观读(如 RCU 风格):考虑 std::atomic<t></t> + hazard pointer,或 absl::ReaderMutex(如果允许引入第三方)
  • 需细粒度控制(如分段锁):用 std::Array<:shared_mutex n></:shared_mutex> 分片保护容器,比单个锁扩展性更好

真正难的从来不是选对锁类型,而是识别出“这里其实根本不需要锁”——比如用无锁队列、copy-on-write vector 或只读快照,往往比调优 shared_mutex 更有效。

text=ZqhQzanResources