24.多线程为什么会发生死锁,死锁是什么?死锁产生的条件,如何解决死锁?

beat365在线官网 📅 2025-08-12 23:45:13 ✍️ admin 👁️ 4189 ❤️ 160
24.多线程为什么会发生死锁,死锁是什么?死锁产生的条件,如何解决死锁?

1. 死锁(Deadlock)是什么?

死锁是指多个线程(或进程)在执行过程中,因争夺资源而陷入互相等待的状态,导致所有线程都无法继续执行,程序卡死。

举例

线程A持有锁1,并尝试获取锁2;线程B持有锁2,并尝试获取锁1;结果:两个线程互相等待对方释放锁,程序无法继续运行。

2. 死锁产生的四个必要条件(Coffman条件)

死锁的发生必须同时满足以下四个条件,只要破坏其中一个,就能避免死锁:

条件解释互斥条件资源一次只能被一个线程占用(如锁、文件句柄等)。占有并等待线程持有至少一个资源,并等待获取其他被占用的资源。不可抢占已分配给线程的资源不能被其他线程强行夺取,必须由持有者主动释放。循环等待存在一个线程等待环路(如A等B,B等A)。3. 死锁的解决方案

(1) 预防死锁(破坏必要条件)

破坏互斥条件: 某些资源可以改为共享访问(如只读数据),但多数资源(如锁)必须互斥,难以完全避免。破坏占有并等待:

一次性申请所有资源:线程在运行前申请全部所需资源,否则不执行(如 std::lock 同时获取多个锁)。超时机制:尝试获取锁时设置超时,超时后释放已持有的锁(如 try_lock_for)。

破坏不可抢占: 允许系统强制剥夺资源(如数据库事务回滚),但实现复杂,可能引发数据不一致。破坏循环等待:

按固定顺序获取锁:所有线程必须按相同顺序申请资源(如先锁A再锁B)。层级锁:将资源分层,线程必须按层级顺序加锁。

(2) 避免死锁(运行时检测)

银行家算法: 系统动态检查资源分配是否会导致死锁,仅在安全时分配资源(适用于已知资源总量的场景)。死锁检测与恢复:

定期检测是否存在循环等待,若发现则终止某些线程或回滚操作(如数据库系统)。

(3) 忽略死锁

某些场景(如短期运行程序)可接受死锁,直接重启程序(如开发调试阶段)。

4. 代码示例:死锁 vs 解决方案

(a) 典型死锁(循环等待)

#include

#include

#include

std::mutex mtx1, mtx2;

void threadA() {

mtx1.lock();

std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟耗时操作

mtx2.lock(); // 等待线程B释放mtx2

std::cout << "Thread A finished\n";

mtx2.unlock();

mtx1.unlock();

}

void threadB() {

mtx2.lock();

std::this_thread::sleep_for(std::chrono::milliseconds(100));

mtx1.lock(); // 等待线程A释放mtx1

std::cout << "Thread B finished\n";

mtx1.unlock();

mtx2.unlock();

}

int main() {

std::thread t1(threadA);

std::thread t2(threadB);

t1.join();

t2.join(); // 程序卡死!

return 0;

}

(b) 解决方案1:按固定顺序加锁

void threadA() {

mtx1.lock();

mtx2.lock(); // 所有线程必须先锁mtx1,再锁mtx2

std::cout << "Thread A finished\n";

mtx2.unlock();

mtx1.unlock();

}

void threadB() {

mtx1.lock(); // 遵守相同顺序

mtx2.lock();

std::cout << "Thread B finished\n";

mtx2.unlock();

mtx1.unlock();

}

© 解决方案2:使用 std::lock 一次性获取多个锁

void threadA() {

std::lock(mtx1, mtx2); // 原子化获取多个锁

std::cout << "Thread A finished\n";

mtx2.unlock();

mtx1.unlock();

}

5. 实际开发中的建议

尽量减少锁的持有时间(如缩小临界区范围)。避免嵌套锁:如果必须用多个锁,确保所有线程按相同顺序加锁。使用RAII管理锁(如 std::lock_guard、std::unique_lock),避免忘记解锁。考虑无锁编程:如使用原子操作(std::atomic)或线程安全数据结构。

总结

问题关键点死锁定义多个线程互相等待资源,导致程序卡死。四个必要条件互斥、占有并等待、不可抢占、循环等待(破坏任一个即可避免死锁)。解决方案预防(破坏条件)、避免(银行家算法)、检测恢复、忽略。最佳实践按顺序加锁、缩短锁生命周期、使用RAII、无锁编程。

相关推荐

yue拼音的字有哪些
beat365在线官网

yue拼音的字有哪些

📅 07-27 👁️ 1286
烀牛肉的做法与步骤
365bet官网注册

烀牛肉的做法与步骤

📅 07-09 👁️ 2801
《方舟:飛昇》延至2023年10月推出、更新路線圖公佈、FAQ @方舟 系列 哈啦板