C++死锁剖析
·
目录
✅ 2. 使用 std::scoped_lock(C++17 推荐)
🧩 一、什么是死锁(Deadlock)
定义:
死锁是指两个或多个线程在执行过程中,因争夺资源而造成的一种相互等待的情况。
如果没有外力干预,它们将永远无法继续执行。
简单说就是:
“你等我释放锁,我等你释放锁,大家都不动了。”
⚙️ 二、最常见的死锁场景
🔹例子 1:两个线程交叉拿锁
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
std::mutex m1, m2;
void task1() {
std::lock_guard<std::mutex> lock1(m1);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::lock_guard<std::mutex> lock2(m2);
cout << "任务1完成" << endl;
}
void task2() {
std::lock_guard<std::mutex> lock2(m2);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::lock_guard<std::mutex> lock1(m1);
cout << "任务2完成" << endl;
}
int main() {
std::thread t1(task1);
std::thread t2(task2);
t1.join();
t2.join();
}
💥结果:
-
线程 1:先锁
m1,等m2; -
线程 2:先锁
m2,等m1; -
两个都不释放 → 死锁!
🧠 类比现实:
我拿了你的手机,你拿了我的钱包,
我说“你先给我钱包”,你说“你先给我手机”,
然后我们俩就杵那儿不动了。
🧠 三、死锁的四个必要条件(非常重要)
要产生死锁,必须同时满足以下四个条件👇
只要破坏其中一个,就不会死锁。
| 条件 | 解释 |
|---|---|
| ① 互斥条件 | 资源不能同时被多个线程使用(比如 mutex) |
| ② 占有且等待 | 一个线程拿着一个锁,又去申请另一个锁 |
| ③ 不可剥夺 | 已获得的资源不能被强制夺走,只能自己释放 |
| ④ 循环等待 | 存在一个线程等待链:A 等 B,B 等 C,C 等 A |
💡 死锁避免的关键就是:破坏“循环等待”或“占有且等待”。
🧯 四、如何避免死锁(重点)
✅ 1. 固定加锁顺序
给所有锁编号,所有线程都按同样顺序加锁。
void safeTask1() {
std::lock(m1, m2); // 一次性锁住多个锁,避免交叉
std::lock_guard<std::mutex> lock1(m1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(m2, std::adopt_lock);
cout << "安全任务1完成\n";
}
📌 std::lock() 是 C++ 提供的安全加锁工具,它保证不会产生死锁。
✅ 2. 使用 std::scoped_lock(C++17 推荐)
void safeTask2() {
std::scoped_lock lock(m1, m2); // 一次性锁多个mutex
cout << "安全任务2完成\n";
}
👉 std::scoped_lock 会自动处理加锁顺序问题,最安全!
✅ 3. 尽量减少锁的粒度
锁定的代码范围越小,死锁几率越低。
{
std::lock_guard<std::mutex> lock(m1);
// 临界区尽可能短
}
✅ 4. 尝试非阻塞锁 try_lock()
if (m1.try_lock()) {
if (m2.try_lock()) {
// 同时拿到锁
m2.unlock();
}
m1.unlock();
}
💡 如果拿不到锁,线程不会阻塞等待,而是可以选择重试或放弃。
🧩 五、如何检测或调试死锁
在实际项目中,死锁往往隐蔽且难复现。可用以下方法:
-
工具检测:如 Clang ThreadSanitizer、Visual Studio Concurrency Visualizer。
-
日志追踪:打印加锁/解锁信息,分析线程卡在哪里。
-
超时机制:用
try_lock_for()(C++14)设置锁等待超时。
🎯 六、总结一页表
| 项目 | 说明 |
|---|---|
| 死锁定义 | 多线程互相等待资源释放,无法继续执行 |
| 常见场景 | 不同线程锁资源顺序不一致 |
| 四个必要条件 | 互斥、占有且等待、不可剥夺、循环等待 |
| 主要预防方法 | 固定加锁顺序 / scoped_lock / 减少锁粒度 / try_lock |
| 检测手段 | ThreadSanitizer、日志、超时机制 |
更多推荐


所有评论(0)