Qt篇: 多线程死锁

造成死锁的原因

一般性原则,每个临界资源都需要一个线程锁来进行保护,假设有多个线程锁该怎么设计程序呢?先来看以下的一个示例(代码很简单就不加注释了):

#include <QCoreApplication>
#include <QThread>
#include <QMutex>
#include <QDebug>
 
int g_i_current_data = 0;
QMutex g_mutex_1;
QMutex g_mutex_2;

class ThreadMutexA : public QThread
{
protected:
    void run()
    {
        qDebug() << objectName() << ": run begin...";
        while( true )
        {
            g_mutex_1.lock();
            qDebug() << objectName() << "get mutex_1...";
 
            g_mutex_2.lock();
            qDebug() << objectName() << "get mutex_2...";
 
            g_i_current_data++;
            qDebug() << objectName() << ": " << g_i_current_data;
 
            g_mutex_2.unlock();
            g_mutex_1.unlock();
            msleep(1);
        }
        qDebug() << objectName() << ": run end...";
    }
};
 
class ThreadMutexB : public QThread
{
protected:
    void run()
    {
        qDebug() << objectName() << ": run begin...";
        while( true )
        {
            g_mutex_2.lock();
            qDebug() << objectName() << "get mutex_2...";
 
            g_mutex_1.lock();
            qDebug() << objectName() << "get mutex_1...";
 
            g_i_current_data--;
            qDebug() << objectName() << ": " << g_i_current_data;
 
            g_mutex_1.unlock();
            g_mutex_2.unlock();
            msleep(1);
        }
        qDebug() << objectName() << ": run end...";
    }
};
 
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug() << "main begin...";
    ThreadMutexA thread_a;
    ThreadMutexA thread_a1;
    ThreadMutexA thread_a2;
    ThreadMutexB thread_b;
    ThreadMutexB thread_b1;
 
    thread_a.setObjectName("ThreadMutexA");
    thread_a1.setObjectName("ThreadMutexA_1");
    thread_a2.setObjectName("ThreadMutexA_2");
    thread_b.setObjectName("ThreadMutexB");
    thread_b1.setObjectName("ThreadMutexB_1");
 
    thread_a.start();
    thread_a1.start();
    thread_a2.start();
    thread_b.start();
    thread_b1.start();
 
    qDebug() << "main end...";
    return a.exec();
}
死锁现象

现象分析

  • 现象:五个线程分别都进入了run()函数中,线程锁1和线程锁2分别被线程A和线程B获取(这两个线程动作比较快优先获取到线程数)后,程序就阻塞在这里了,不继续往下执行了。
  • 分析
    1. 当线程A获取线程锁1时,就要继续获取线程锁2,但是此时线程锁2被线程B获取,线程A就只好等待线程锁2被释放;
    2. 线程B获取线程锁2后,需要继续获取线程锁1,而此时线程锁1被线程A锁获取并没有释放,线程B也就只好等待线程A释放线程锁1了;
    3. 两个线程一直等待彼此释放相应线程锁,等到花都谢了也等不到,程序无法继续执行。
  • 原因:造成了线程的死锁

死锁的概念和发生条件

  • 死锁概念:多线程之间相互等待临界资源而造成彼此无法继续执行。
  • 死锁发生条件
    1. 系统中存在多个临界资源且临界资源不可抢占;
    2. 每个线程需要多个临界资源才能继续执行。
死锁原因

死锁的避免

  1. 对所有临界资源都分配唯一的序号(R1, R2, R3, ..., Rn);
  2. 对应的线程锁也分配同样的序号(M1, M2, M3, ..., Mn);
  3. 系统中的每个线程必须按照严格的升序的次序去请求资源。
    ThreadMutexA A;
    mutex_1.lock();
    mutex_2.lock();
    mutex_3.lock();
    mutex_4.lock();

避免死锁完整示例代码

#include <QCoreApplication>
#include <QThread>
#include <QMutex>
#include <QDebug>
 
int g_i_current_data = 0;
QMutex g_mutex_1;
QMutex g_mutex_2;
 
class ThreadMutexA : public QThread
{
protected:
    void run()
    {
        qDebug() << objectName() << ": run begin...";
        while( true )
        {
            g_mutex_1.lock();
            qDebug() << objectName() << "get mutex_1...";
 
            g_mutex_2.lock();
            qDebug() << objectName() << "get mutex_2...";
 
            g_i_current_data++;
            qDebug() << objectName() << ": " << g_i_current_data;
 
            g_mutex_2.unlock();
            g_mutex_1.unlock();
            msleep(1);
        }
        qDebug() << objectName() << ": run end...";
    }
};
 
class ThreadMutexB : public QThread
{
protected:
    void run()
    {
        qDebug() << objectName() << ": run begin...";
        while( true )
        {
            g_mutex_1.lock();
            qDebug() << objectName() << "get mutex_1...";
 
            g_mutex_2.lock();
            qDebug() << objectName() << "get mutex_2...";
 
            g_i_current_data--;
            qDebug() << objectName() << ": " << g_i_current_data;
 
            g_mutex_2.unlock();
            g_mutex_1.unlock();
            msleep(1);
        }
        qDebug() << objectName() << ": run end...";
    }
};
 
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug() << "main begin...";
 
    ThreadMutexA thread_a;
    ThreadMutexA thread_a1;
    ThreadMutexA thread_a2;
    ThreadMutexB thread_b;
    ThreadMutexB thread_b1;
 
    thread_a.setObjectName("ThreadMutexA");
    thread_a1.setObjectName("ThreadMutexA_1");
    thread_a2.setObjectName("ThreadMutexA_2");
    thread_b.setObjectName("ThreadMutexB");
    thread_b1.setObjectName("ThreadMutexB_1");
 
    thread_a.start();
    thread_a1.start();
    thread_a2.start();
    thread_b.start();
    thread_b1.start();
 
    qDebug() << "main end...";
    return a.exec();
}
死锁解决