目录

🧩 一、什么是死锁(Deadlock)

⚙️ 二、最常见的死锁场景

🔹例子 1:两个线程交叉拿锁

💥结果:

🧠 三、死锁的四个必要条件(非常重要)

🧯 四、如何避免死锁(重点)

✅ 1. 固定加锁顺序

✅ 2. 使用 std::scoped_lock(C++17 推荐)

✅ 3. 尽量减少锁的粒度

✅ 4. 尝试非阻塞锁 try_lock()

🧩 五、如何检测或调试死锁

🎯 六、总结一页表


🧩 一、什么是死锁(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、日志、超时机制

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐