必须用RAII管理std::mutex:优先std::lock_guard(简单场景)或std::unique_lock(需try_lock/condition_variable等),禁用手动lock/unlock;多锁须按固定顺序或用std::scoped_lock避免死锁;shared_mutex慎用于写频繁场景,且不可复制移动。

std::mutex 必须配合 std::lock_guard 或 std::unique_lock 使用
直接调用 mutex.lock() 和 mutex.unlock() 极易出错——比如异常抛出后忘记解锁,导致死锁。C++ 标准库不鼓励手动管理锁的生命周期。
正确做法是依赖 RAII:用 std::lock_guard(作用域自动加锁/解锁)或 std::unique_lock(支持延迟锁定、转移所有权等高级操作)。
-
std::lock_guard更轻量,适用于“进作用域就锁、出作用域就放”的简单场景 -
std::unique_lock开销略大,但支持try_lock()、unlock()手动释放、与std::condition_variable配合 - 绝不要在同一个线程中对已持有的
std::mutex再次调用lock()——这是未定义行为,不是可重入锁
多个 mutex 按固定顺序加锁,避免死锁
当一段逻辑需要同时持有两个或以上互斥锁时,如果线程 A 先锁 mtx1 再锁 mtx2,而线程 B 反过来先锁 mtx2 再锁 mtx1,就可能形成环形等待。
解决方法是全局约定加锁顺序(例如按地址大小、按变量声明顺序),或使用 std::scoped_lock(C++17 起)一次性按无序方式安全加锁:
立即学习“C++免费学习笔记(深入)”;
std::mutex mtx1, mtx2; // 安全:自动按内部顺序加锁,不会死锁 std::scoped_lock lock(mtx1, mtx2);
-
std::scoped_lock是std::lock_guard的多锁升级版,构造即加锁,析构即解锁 - 旧代码若用
std::lock(mtx1, mtx2)+std::lock_guard手动构造,容易漏掉adopt_lock参数,导致重复加锁崩溃 - 别用
std::try_lock循环抢锁代替顺序约定——它只是绕开问题,不能根除竞争逻辑缺陷
shared_mutex 适合读多写少场景,但注意 writer starvation
std::shared_mutex(C++17)允许多个 reader 并发访问,writer 独占访问。但它不保证公平性:持续的 reader 请求可能让 writer 无限等待。
如果你的场景写操作频率不低,或对写延迟敏感,std::shared_mutex 反而比普通 std::mutex 更慢、更难调试。
- reader 使用
std::shared_lock<:shared_mutex> - writer 使用
std::unique_lock<:shared_mutex> - Windows 上对应的是
SRWLOCK,Linux glibc 实现早期有性能问题,建议用较新编译器(GCC 9+/Clang 8+) - 不要在持有 shared_lock 期间调用可能阻塞或抛异常的函数——否则 reader 长时间不释放,会拖垮 writer
std::mutex 不能跨线程复制或移动,只支持默认构造和销毁
std::mutex 是不可复制、不可移动的类型。试图把它放进 std::vector<:mutex> 或作为结构体成员被拷贝,编译直接报错:
error: use of deleted function 'std::mutex::mutex(const std::mutex&)'
常见误用包括:
- 把
std::mutex当作普通成员变量,在类赋值运算符中没显式忽略它 - 想用
std::make_shared创建含 mutex 的对象,却忘了() MyClass的构造函数必须显式初始化 mutex(默认构造即可) - 在线程函数里传入局部
std::mutex的引用——该 mutex 随函数返回被销毁,其他线程再访问就是 dangling reference
真正需要“多个锁”时,应静态分配、全局唯一,或用指针/智能指针管理生命周期,而不是尝试复制锁本身。











