死锁的概念

什么是死锁?

程序流程无法继续推进卡死的状态

死锁产生条件

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;
}

android防止死锁 安卓死锁_android防止死锁

此时两个线程分别获得了一个锁之后程序停下来,不往下继续执行了,一直等待在这里。那么该如何去分析它是什么问题导致的呢?

程序一直等待有可能的情况:死循环,等待输入(cin,scanf),死锁等等

1.首先我们先去查看该进程处于什么状态

android防止死锁 安卓死锁_android防止死锁_02

可以看到是Sl+状态 百度得知是 其处于休眠状态,多线程,且是后台进程 也就是说这个多线程程序都进入阻塞状态。

2.现在再看一下进程中的线程的运行情况:top -Hp PID

android防止死锁 安卓死锁_死锁案例_03


  可以看到所有线程都是处于阻塞状态,并且CPU利用率为0,那么就排除是死循环的情况。

3.再看一下线程的堆栈调用信息

进行远程gdb调试正在运行的程序,gdb attach PID进入gdb命令行 查看该进程的每一个线程的堆栈信息  thread apply all bt

android防止死锁 安卓死锁_android防止死锁_04

可以看到Thread1与Thread2最后都停在了 lock_wait这里,也就是说,两个线程都在等待获取一把锁,结合源代码分析,thread1和thread2都想获取对方手里的锁,但是获取不到就一直等待这里,阻塞住了。

android防止死锁 安卓死锁_死锁产生_05

从源码定位问题

1.编译debug版本

android防止死锁 安卓死锁_死锁_06

2.运行程序并远程gdb调试

android防止死锁 安卓死锁_分析解决死锁_07

android防止死锁 安卓死锁_android防止死锁_08

3.查看当前所有线程信息

android防止死锁 安卓死锁_死锁_09

此时在线程1也就是main线程处,先切换线程

4.切换线程到thread2或者thread3 并打印堆栈信息

android防止死锁 安卓死锁_分析解决死锁_10

5.查看线程2的第4帧信息 

android防止死锁 安卓死锁_分析解决死锁_11

可以看到源代码停在了第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;
}

android防止死锁 安卓死锁_死锁_12