第76课 多线程间的互斥(下)

一、多线程间的互斥

            程序有多少临界资源?    需要多少线程锁?

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

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


    2、 死锁的概念

        2.1、 线程间相互等待资源而造成彼此无法继续执行(只用一个线程锁,就不会发生死锁了,客户端可以使用)
    

    3、 发生死锁的条件

        3.1、 系统中存在多个临界资源且临界资源不可抢占(每次只有一个线程使用)
        3.2、 线程需要多个临界资源才能继续执行
                    

死锁的产生:

#include <QtCore/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.........A";
            g_mutex_2.unlock();
            g_mutex_1.unlock();
        }
    }
};
class ThreadB : public QThread
{
    void run()
    {
        while(true)
        {
            g_mutex_2.lock();
            g_mutex_1.lock();
            qDebug() << objectName() << "doing  work.........B";
            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();
    
    qDebug() << "main()   end";
    return a.exec();
}


    4、 死锁的避免
        4.1、 对所有的临界资源都分配唯一一个序号(r1,r2,r3,...,rn)
        4.2、 对应的线程锁也分配同样的序号(m1,m2,m3,..., mn)
        4.3、 系统中的每个线程按照严格递增的次序请求资源
                

上一个死锁例子的改进:

#include <QtCore/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.........A";
            g_mutex_2.unlock();
            g_mutex_1.unlock();
        }
    }
};
class ThreadB : public QThread
{
    void run()
    {
        while(true)
        {
            g_mutex_1.lock();           //都是递增获取资源
            g_mutex_2.lock();
            qDebug() << objectName() << "doing  work.........B";
            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();
    
    qDebug() << "main()   end";
    return a.exec();
}

5、 信号量的概念

    5.1、 信号量是特殊的线程锁
    5.2、 信号量允许多个线程同时访问临界资源
    5.3、 Qt中直接支持信号量(QSemaphore)
                
    
使用信号量解决多个生产者消费者的问题:


#include <QtCore/QCoreApplication>
#include <QThread>
#include <QSemaphore>
#include <QDebug>
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;    //value在0 - 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();//消费了一个产品,可以消费的产品就要减一。
            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、 信号量允许多个线程同时访问临界资源








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值