第七十六课、多线程间的互斥(下)------------------狄泰软件学院

一、多线程间的互斥

1、程序的临界资源与线程锁的数量关系

(1)、一般性原则:每一个临界资源都需要一个线程锁进行保护

 2、死锁的概念

(1)、线程间相互等待资源而造成彼此无法继承执行

3、发生死锁的条件

(1)、系统中存在多个临界资源且临界资源不可抢占(每次只有一个线程使用)

(2)、线程需要多个临界资源才能继续执行

#include <QCoreApplication>
#include <QThread>
#include <QMutex>
#include <QDebug>

static QMutex g_mutex_1;
static QMutex g_mutex_2;

class ThreadA : public QThread
{
protected:
    void run()
    {
        while(true)
        {
            g_mutex_1.lock();//获取第一把锁后就会去等待第二把锁,但第二把锁可能已经被另一个线程获取,会一直死等
            g_mutex_2.lock();

            qDebug() << objectName() << " doing work";


            g_mutex_2.unlock();
            g_mutex_1.unlock();
        }
    }
};

class ThreadB : public QThread
{
protected:
    void run()
    {
        while(true)
        {
            g_mutex_2.lock();
            g_mutex_1.lock();

            qDebug() << objectName() << " doing work";


            g_mutex_1.unlock();
            g_mutex_2.unlock();
        }
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    ThreadA ta;
    ThreadB tb;

    ta.setObjectName("ta");
    tb.setObjectName("tb");

    ta.start();
    tb.start();

    return a.exec();
}
死锁的产生

4、死锁的避免

(1)、对所有的临界资源都分配唯一一个序号(r1,r2,r3,...,rn)

(2)、对应的线程锁也分配同样的序号(m1,m2,m3,..., mn)

(3)、系统中的每个线程按照严格递增的次序请求资源

#include <QCoreApplication>
#include <QThread>
#include <QMutex>
#include <QDebug>

static QMutex g_mutex_1;
static QMutex g_mutex_2;

class ThreadA : public QThread
{
protected:
    void run()
    {
        while(true)
        {
            g_mutex_1.lock();
            g_mutex_2.lock();

            qDebug() << objectName() << " doing work";


            g_mutex_2.unlock();
            g_mutex_1.unlock();
        }
    }
};

class ThreadB : public QThread
{
protected:
    void run()
    {
        while(true)
        {
            g_mutex_1.lock();//都是递增获取资源
            g_mutex_2.lock();

            qDebug() << objectName() << " doing work";


            g_mutex_2.unlock();
            g_mutex_1.unlock();
        }
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    ThreadA ta;
    ThreadB tb;

    ta.setObjectName("ta");
    tb.setObjectName("tb");

    ta.start();
    tb.start();

    return a.exec();
}
改进:都是递增获取临界资源

5、信号量的概念

(1)、信号量是特殊的线程锁

(2)、信号量允许多个线程同时访问临界资源

(3)、Qt中直接支持信号量(QSemaphore)

 

#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <QSemaphore>

const int SIZE = 5;
unsigned char g_buff[SIZE] = {0};
QSemaphore g_sem_free(SIZE);
QSemaphore g_sem_used(0);

class Producer : public QThread
{
protected:
    void run()
    {
        while(true)
        {
            int value = qrand() % 256;

            g_sem_free.acquire();//生产了一个产品,需要生产的数量减1

            for(int i=0; i<SIZE; i++)
            {
                if( !g_buff[i] )
                {
                    g_buff[i] = value;

                    qDebug() << objectName() << " generate: {" << i << ", " << value << "}";

                    break;
                }
            }

            g_sem_used.release();//生产了一个产品,可用的产品就增1
            sleep(1);

        }
    }
};

class Customer : public QThread
{
protected:
    void run()
    {
        while(true)
        {

            g_sem_used.acquire();//消费了一个产品,可消费的产品就减1

            for(int i=0; i<SIZE; i++)
            {
                if( g_buff[i] )
                {
                    int value = g_buff[i];

                    g_buff[i] = 0;

                    qDebug() << objectName() << " generate: {" << i << ", " << value << "}";

                    break;
                }
            }

            g_sem_free.release();//消费了一个产品,所需要生产的产品就增1
            sleep(2);
        }
    }
};


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Producer p1;
    Producer p2;
    Producer p3;

    p1.setObjectName("p1");
    p2.setObjectName("p2");
    p3.setObjectName("p3");

    Customer c1;
    Customer c2;

    c1.setObjectName("c1");
    c2.setObjectName("c2");

    p1.start();
    p2.start();
    p3.start();

    c1.start();
    c2.start();

    return a.exec();
}
使用信号量解决多个生产消费者的问题

二、小结

(1)、多线程间相互等待资源导致死锁

(2)、可以对临界资源进行编号的方法避免死锁

(3)、所有线程必须按照严格递增的次序请求资源

(4)、Qt中直接支持信号量(QSemaphore)

(5)、信号量允许多个线程同时访问临界资源

原文地址:https://www.cnblogs.com/gui-lin/p/6444087.html