如何在C++中实现简单死锁?附相关代码片段
这段C++代码能触发死锁吗?
答案是肯定的,这段代码完全踩中了死锁的所有必要条件,几乎每次运行都会直接卡死。
先把你没写完的代码补全为可运行的完整版本:
#include <iostream> #include <mutex> #include <thread> #include <chrono> std::mutex m1; std::mutex m2; auto f1 = [&m1, &m2]() { std::lock_guard<std::mutex> lg1(m1); std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::lock_guard<std::mutex> lg2(m2); std::cout << "A"; }; auto f2 = [&m1, &m2]() { std::lock_guard<std::mutex> lg1(m2); std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::lock_guard<std::mutex> lg2(m1); std::cout << "B"; }; int main() { std::thread t1(f1); std::thread t2(f2); t1.join(); t2.join(); return 0; }
为什么会触发死锁?
死锁的四个必要条件这段代码全满足:
- 互斥条件:
std::mutex本身就是互斥资源,同一时间只能被一个线程持有; - 持有并等待:线程1拿到
m1后,等着拿m2;线程2拿到m2后,等着拿m1; - 不可剥夺:
std::lock_guard持有的锁不能被其他线程强行夺走,只能由持有线程主动释放; - 循环等待:线程1依赖线程2持有的
m2,线程2依赖线程1持有的m1,形成了循环依赖链。
中间的sleep_for更是给死锁上了“保险”——它确保两个线程有足够的时间分别拿到第一个锁,然后同时卡在第二个锁的获取步骤上,几乎不会出现侥幸绕过的情况。
怎么避免这个死锁?
最常用的两种解决方案:
- 固定锁的获取顺序:让两个线程都严格按照先
m1后m2的顺序获取锁,直接打破循环等待的条件; - 用
std::lock批量获取锁:这个函数内部会用避免死锁的算法来获取多个锁,示例代码如下:auto f1 = [&m1, &m2]() { std::lock(m1, m2); std::lock_guard<std::mutex> lg1(m1, std::adopt_lock); std::lock_guard<std::mutex> lg2(m2, std::adopt_lock); std::cout << "A"; }; auto f2 = [&m1, &m2]() { std::lock(m1, m2); std::lock_guard<std::mutex> lg1(m1, std::adopt_lock); std::lock_guard<std::mutex> lg2(m2, std::adopt_lock); std::cout << "B"; };
内容的提问来源于stack exchange,提问作者Witnes
相关产品推荐
相关产品推荐

