std::shared_mutex无写锁时无法获取读锁问题排查
代码示例
#include "shared_mutex" #include "thread" #include "iostream" #include "string" using namespace std::chrono_literals; struct debug_mutex : public std::shared_mutex { void lock() noexcept { std::string m = "Try To lock, u_count: " + std::to_string(unique_lock_c.load()) + ", s_count" + std::to_string(shared_lock_c.load()) + "\n"; std::cout << m << std::endl; std::shared_mutex::lock(); unique_lock_c.fetch_add(1); std::string m1 = "lock, u_count: " + std::to_string(unique_lock_c.load()) + ", s_count" + std::to_string(shared_lock_c.load()) + "\n"; std::cout << m1 << std::endl; } void unlock() noexcept { std::string m = "Try To unlock, u_count: " + std::to_string(unique_lock_c.load()) + ", s_count" + std::to_string(shared_lock_c.load()) + "\n"; std::cout << m << std::endl; std::shared_mutex::unlock(); unique_lock_c.fetch_add(-1); std::string m1 = "unlock, u_count: " + std::to_string(unique_lock_c.load()) + ", s_count" + std::to_string(shared_lock_c.load()) + "\n"; std::cout << m1 << std::endl; } void lock_shared() noexcept { std::string m = "Try To lock_shared, u_count: " + std::to_string(unique_lock_c.load()) + ", s_count" + std::to_string(shared_lock_c.load()) + "\n"; std::cout << m << std::endl; std::shared_mutex::lock_shared(); shared_lock_c.fetch_add(1); std::string m1 = "lock_shared, u_count: " + std::to_string(unique_lock_c.load()) + ", s_count" + std::to_string(shared_lock_c.load()) + "\n"; std::cout << m1 << std::endl; } void unlock_shared() noexcept { std::string m = "Try To unlock_shared, u_count: " + std::to_string(unique_lock_c.load()) + ", s_count" + std::to_string(shared_lock_c.load()) + "\n"; std::cout << m << std::endl; std::shared_mutex::unlock_shared(); shared_lock_c.fetch_add(-1); std::string m1 = "unlock_shared, u_count: " + std::to_string(unique_lock_c.load()) + ", s_count" + std::to_string(shared_lock_c.load()) + "\n"; std::cout << m1 << std::endl; } std::atomic_uint64_t shared_lock_c{0}; std::atomic_uint64_t unique_lock_c{0}; } mutex; int main(int argc, char **argv) { std::thread t{[]() { std::this_thread::sleep_for(10ns); while (true) { mutex.lock_shared(); mutex.lock_shared(); mutex.lock_shared(); mutex.lock_shared(); mutex.lock_shared(); mutex.lock_shared(); std::this_thread::sleep_for(5ns); mutex.unlock_shared(); mutex.unlock_shared(); mutex.unlock_shared(); mutex.unlock_shared(); mutex.unlock_shared(); mutex.unlock_shared(); std::cout << "read lock once" << std::endl; } }}; std::thread t1{[]() { std::this_thread::sleep_for(5ms); while (true) { mutex.lock(); std::this_thread::sleep_for(5ms); mutex.unlock(); std::cout << "write lock once" << std::endl; } }}; t.join(); t1.join(); }
问题现象
代码预期读写锁正常交替、持续运行,但实际出现异常阻塞,阻塞时日志如下:
Try To lock_shared, u_count: 0, s_count4
lock_shared, u_count: 0, s_count5
Try To lock, u_count: 0, s_count5
Try To lock_shared, u_count: 0, s_count5
问题原因
- std::shared_mutex的写优先特性:C++标准规定
std::shared_mutex是写优先的——当有写请求在等待时,新的读请求会被阻塞,防止写请求长期饥饿。此场景中:- 读线程已持有多个共享锁(日志显示
s_count=5),写线程发起lock()请求后进入等待队列; - 读线程循环中再次调用
lock_shared(),此时因写请求排队,新读请求被阻塞;而写线程又因读锁未完全释放无法获取排他锁,最终形成死锁。
- 读线程已持有多个共享锁(日志显示
- 读线程重复加锁的不合理设计:同一读线程在未释放共享锁的情况下连续多次调用
lock_shared(),虽然shared_mutex允许同一线程重复获取共享锁,但这种用法放大了写请求等待时的阻塞风险,直接触发了死锁场景。
修复方案
要让程序持续运行直至手动终止,可按以下方式调整:
1. 移除读线程的重复加锁操作
读线程只需一次共享锁即可保护整个读操作周期,无需重复加锁。调整后的读线程代码:
std::thread t{[]() { std::this_thread::sleep_for(10ns); while (true) { mutex.lock_shared(); std::this_thread::sleep_for(5ns); mutex.unlock_shared(); std::cout << "read lock once" << std::endl; } }};
2. (可选)修复日志输出的线程安全问题
原代码中多线程直接使用std::cout会导致日志混乱,建议给输出加独立互斥锁:
#include <mutex> std::mutex cout_mutex; // 在所有输出日志的位置,用锁包裹: std::lock_guard<std::mutex> lock(cout_mutex); std::cout << m << std::endl;
3. (业务可选)自定义读优先读写锁
如果业务场景需要读优先而非标准库的写优先,可以自行实现读优先的读写锁逻辑,避免写请求排队时阻塞新读请求的情况。
内容的提问来源于stack exchange,提问作者MikuSoft
相关产品推荐
相关产品推荐

