Qt 笔记:多线程间的互斥(下)

线程的死锁概念

-线程间相互等待临界资源而造成批次无法继续执行

发生死锁的条件

-系统中存在多个临界资源且临界资源不可抢占

-线程需要多个临界资源才能继续执行

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

QMutex g_mutex_1;
QMutex g_mutex_2;

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

            qDebug() << objectName() << "get m1";

            g_mutex_2.lock();

            qDebug() << objectName() << "get m2";

            qDebug() << objectName() << "do work ...";

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


    }
};

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

            qDebug() << objectName() << "get m2";

            g_mutex_1.lock();

            qDebug() << objectName() << "get m1";

            qDebug() << objectName() << "do work ...";

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


    }
};

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

    ThreadA a;
    ThreadB b;

    a.setObjectName("a");
    b.setObjectName("b");

    a.start();
    b.start();

    return _a.exec();
}

死锁的避免

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

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

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

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

QMutex g_mutex_1;
QMutex g_mutex_2;

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

            qDebug() << objectName() << "get m1";

            g_mutex_2.lock();

            qDebug() << objectName() << "get m2";

            qDebug() << objectName() << "do work ...";

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


    }
};

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

            qDebug() << objectName() << "get m2";

            g_mutex_2.lock();

            qDebug() << objectName() << "get m1";

            qDebug() << objectName() << "do work ...";

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


    }
};

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

    ThreadA a;
    ThreadB b;

    a.setObjectName("a");
    b.setObjectName("b");

    a.start();
    b.start();

    return _a.exec();
}

信号量的概念

-信号量是特殊的线程锁

-信号量允许N个线程同时访问临界资源

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

ps:QSemaphore对象中维护了一个整形值,acquire()是的该值减1,release()使得该值加1,当该值为0时,acquire()函数将阻塞当前线程

#include <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;

            g_sem_free.acquire();

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

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() << "consume: (" << i << "," << value << ")";

                    break;
                }
            }
            g_sem_free.release();

            sleep(2);
        }
    }
};

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

    Producer p1;
    Producer p2;
    Producer p3;

    p1.setObjectName("p1");
    p1.setObjectName("p2");
    p1.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();
}

总结:

-多线程间相互等待临界资源将导致死锁

-可以对临界资源进行编号的方法发避免死锁

-所有线程必须按照严格递增的次序请求资源

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

-信号量允许N个线程同时访问临界资源

Qt中实现多线程数据收发的方法如下: 首先,在头文件"datareceiver.h"中定义了一个名为DataReceiver的类,继承自QThread类。该类中包含了一个静态的数据池DataPool,用于存储接收到的数据。同时,该类还定义了一个单例模式的getInstance()函数,用于获取DataReceiver的实例。在构造函数中,可以传入一个QObject类型的父对象。还有一个stop()函数,用于停止线程的执行。在run()函数中,通过循环判断数据池是否为空,如果不为空,则取出第一个数据并发出信号oneDataReady()。 在源文件"datareceiver.cpp"中,首先定义了一个静态的QStringList类型的数据池list,并初始化为空列表。然后使用Q_GLOBAL_STATIC宏定义了一个名为s_DataReceiver的全局静态变量,类型为DataReceiver,用于保存DataReceiver的实例。在getInstance()函数中,返回s_DataReceiver的值。在构造函数中,将父对象传递给QThread的构造函数。stop()函数中,调用requestInterruption()函数请求线程中断。在run()函数中,通过互斥锁mutex保证线程安全,判断数据池是否为空,如果不为空,则取出第一个数据并发出信号oneDataReady()。最后,通过调用msleep()函数短暂睡眠,让出线程。 使用该类的方法是,首先获取DataReceiver的实例,然后调用start()函数启动线程。在接收到数据后,将数据添加到数据池中。可以通过连接信号oneDataReady()来处理接收到的数据。 以上是在Qt中实现多线程数据收发的简要介绍。\[1\]\[3\] #### 引用[.reference_title] - *1* *3* [一种基于Qt多线程的数据接收方案](https://blog.csdn.net/iriczhao/article/details/121503545)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Qt工作笔记-UDP多线程数据处理及发送(简单实例)](https://blog.csdn.net/weixin_39786534/article/details/111545637)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值