死锁的概念
什么是死锁?
程序流程无法继续推进卡死的状态
死锁产生条件
1.互斥条件:我接了锁,别人就不能加锁
2.不可剥夺条件:我加了锁,只有我能解
3.请求与保持条件:加了A锁请求B锁,请求不到B锁 ,A锁不释放
4.环路等待条件:线程1拿了A锁请求B锁,线程2拿了B锁请求A锁
死锁的预防:破坏产生的必要条件
1.破坏条件4,保证加解锁顺序一致
2.使用非阻塞加锁 :拿了A锁请求B锁 请求失败释放A锁
避免死锁
- 破坏死锁的四个必要条件
- 加锁顺序一致
- 避免锁未释放的场景
- 资源一次性分配
避免死锁:银行家算法(银行家算法及代码实现)、死锁检测算法
死锁问题分析及解决
示例
模拟实现死锁问题:线程1已经获取了锁1,线程2已经获取锁2。此时线程1需要获取锁2才可以继续运行,而线程2需要获取锁1才可以继续运行。此时程序就进入死锁状态,代码如下:(使用c++11提供的thread类与mutex编程)
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
mutex m1;
mutex m2;
//线程1获取锁1
void thread1()
{
//获取锁1
m1.lock();
//线程睡眠1秒 在获取锁2 保证线程2能够先获取锁2
this_thread::sleep_for(chrono::seconds(3));
cout << "thread1 获取了mutex1" << endl;
m2.lock();
cout << "thread1 获取了mutex1与mutex2" << endl;
m2.unlock();
m1.unlock();
}
//线程2获取锁2
void thread2()
{
//线程睡眠1秒 在获取锁1 保证线程1能够先获取锁1
this_thread::sleep_for(chrono::seconds(1));
//获取锁2
m2.lock();
cout << "thread2 获取了mutex2" << endl;
//获取锁1
m1.lock();
cout << "thread2 获取了mutex1与mutex2" << endl;
m1.unlock();
m2.unlock();
}
int main()
{
thread t1(thread1), t2(thread2);
t1.join();
t2.join();
return 0;
}
此时两个线程分别获得了一个锁之后程序停下来,不往下继续执行了,一直等待在这里。那么该如何去分析它是什么问题导致的呢?
程序一直等待有可能的情况:死循环,等待输入(cin,scanf),死锁等等
1.首先我们先去查看该进程处于什么状态
可以看到是Sl+状态 百度得知是 其处于休眠状态,多线程,且是后台进程 也就是说这个多线程程序都进入阻塞状态。
2.现在再看一下进程中的线程的运行情况:top -Hp PID
可以看到所有线程都是处于阻塞状态,并且CPU利用率为0,那么就排除是死循环的情况。
3.再看一下线程的堆栈调用信息
进行远程gdb调试正在运行的程序,gdb attach PID进入gdb命令行 查看该进程的每一个线程的堆栈信息 thread apply all bt
可以看到Thread1与Thread2最后都停在了 lock_wait这里,也就是说,两个线程都在等待获取一把锁,结合源代码分析,thread1和thread2都想获取对方手里的锁,但是获取不到就一直等待这里,阻塞住了。
从源码定位问题
1.编译debug版本
2.运行程序并远程gdb调试
3.查看当前所有线程信息
此时在线程1也就是main线程处,先切换线程
4.切换线程到thread2或者thread3 并打印堆栈信息
5.查看线程2的第4帧信息
可以看到源代码停在了第20行。
代码修改
根据上面提到的死锁的预防中几个方法,破坏死锁的环路等待条件就可以了。我们使用上面所说的保证加解锁顺序一致。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
mutex m1;
mutex m2;
//线程1获取锁1
void thread1()
{
//获取锁1
m1.lock();
cout << "thread1 获取了mutex1" << endl;
//获取锁2
m2.lock();
cout << "thread1 获取了mutex2" << endl;
//释放锁
m2.unlock();
m1.unlock();
cout << "thread1 获取了mutex1与mutex2" << endl;
}
//线程2获取锁2
void thread2()
{
//获取锁1
m1.lock();
cout << "thread2 获取了mutex1" << endl;
//获取锁2
m2.lock();
cout << "thread2 获取了mutex2" << endl;
//释放锁
m1.unlock();
m2.unlock();
cout << "thread2 获取了mutex1与mutex2" << endl;
}
int main()
{
thread t1(thread1), t2(thread2);
t1.join();
t2.join();
return 0;
}