首页 > 其他分享 >QT基于互斥锁的线程同步

QT基于互斥锁的线程同步

时间:2025-01-02 23:25:23浏览次数:8  
标签:do QT threadA void 互斥 线程 seq

多线程这个东西越接触越觉得他的强大,我高中的时候就希望自己有几个脑袋,一个看电视,一个玩,一个写作业,一心多用,这个多线程不正好就是的,本次主要是对基于互斥锁的线程同步的研究,显示最基本的互斥锁。

首先是互斥锁的概念,他是来源于生产者/消费者(producer/consumer)模型,比如有一个分线程是负责生产,主线程是消费者,从分线程获取资源,互斥锁 是为了让分线程生产好了一个东西以后再让主线程来获取。

互斥锁有两个类QMutex和QMutexLocker,其中QMutex的实例就是互斥量。QMutex常用的函数有:

lock()锁定互斥量
unlock()解锁一个互斥量
trylock()尝试锁一个互斥量,不行就算了

我个人觉得这就跟指针一样,自己上锁解锁比较容易出错或者忘了,可以像智能指针一样,让他完成以后自己解锁,这就是QMutexLocker的作用。把互斥量作为参数传给QMutexLocker就行了,系统会在分线程完成一次生产后让主线程接受。

读写锁和等待

接下来就是性能优化的问题了这里提出了读写锁的概念,互斥量本身其实是影响程序的性能的,他在生产的过程中会造成堵塞,其他的线程都要等着,应该允许让多个线程访问互斥量。

QReadWritelock主要函数
LockForRead()以只读的方式锁定资源
LockForWrite()以只写的方式锁定资源
unlock()解锁
tryLockForRead()LockForRead()的非阻塞版本
tryLockForWrite()LockForWrite()的非阻塞版本

QReadLocker和QWriteLocker和前面互斥量的QMutexLocker一样,绑定以后不需要手动解锁上锁操作。

基于互斥量的线程同步有几个问题:

第一个问题就是一个线程资源释放了以后,不能及时的通知其他线程

这里引入了QWaitCondition,他的主要函数:

wait(QMutex *lockedmutex)进入等待状态,解锁互斥量lockedmutex,被唤醒后锁定lockedmutex并退出函数
wakeAll()唤醒所有处于等待的线程,唤醒顺序不确定,这个看系统的调度策略
wakeOne()唤醒一个处于等待的线程,唤醒哪一个不确定,这个看系统的调度策略

QWaitCondition一般用于生产者/消费者模型,前面说过了,模型视图如图:

(线程之间的通讯和之前一样采用信号槽的方式)

实例这里我自己没写,就看看书上的源码(Qt6 C++开发指南)

参考上面的模型做的,创建了3个线程,一个生产者TDiceThread,两个消费者TValueThread(获取点数)和TPictureThread(根据获取点数生成图片文件名)

有两个全局变量seq(步数)和dicevalue(点数)

int seq=0, diceValue=0;//seq步数,dicevalue点数

定义实现3个上述的线程

//TDiceThread 是产生骰子点数的线程
class TDiceThread : public QThread
{
    Q_OBJECT
protected:
    void    run();      //线程的任务函数
public:
    explicit TDiceThread(QObject *parent = nullptr);
};


//TValueThread 获取骰子点数
class TValueThread : public QThread
{
    Q_OBJECT
protected:
    void    run();      //线程的任务函数
public:
    explicit TValueThread(QObject *parent = nullptr);
signals:
    void  newValue(int seq, int diceValue);
};


//TPictureThread获取骰子点数,生成对应的图片文件名
class TPictureThread : public QThread
{
    Q_OBJECT
protected:
    void    run();      //线程的任务函数
public:
    explicit TPictureThread(QObject *parent = nullptr);
signals:
    void  newPicture(QString &picName);
};

重写run函数:

void TDiceThread::run()//生产者的run函数重写
{//线程的任务函数
    seq=0;
    while(1)
    {
        rwLocker.lockForWrite();    //以写方式锁定
        diceValue = QRandomGenerator::global()->bounded(1,7);  //产生随机数[1,6]
        seq++;
        rwLocker.unlock();          //解锁
        waiter.wakeAll();       //唤醒其他等待的线程
        msleep(500);    //线程休眠500ms
    }
}

void TValueThread::run()//获取点数的run函数重写
{
    while(1)
    {
        rwLocker.lockForRead();     //以只读方式锁定
        waiter.wait(&rwLocker);     //等待被唤醒
        emit  newValue(seq,diceValue);
        rwLocker.unlock();          //解锁
    }
}
void TPictureThread::run()//通过获取点数生成对应图片文件名的run函数重写
{
    while(1)
    {
        rwLocker.lockForRead();     //以只读方式锁定
        waiter.wait(&rwLocker);     //等待被唤醒
        QString filename=QString::asprintf(":/dice/images/d%d.jpg",diceValue);
        emit  newPicture(filename);
        rwLocker.unlock();          //解锁
    }
}

创建线程并挂到对象树上:

mainwindow.h

private:
    TDiceThread     *threadA;       //producer
    TValueThread    *threadValue;   //consumer 1
    TPictureThread  *threadPic;     //consumer 2

mainwindow.cpp

    threadA= new TDiceThread(this);    //producer
    threadValue= new TValueThread(this);   //consumer 1
    threadPic= new TPictureThread(this);     //consumer 2

线程之间使用信号槽机制通讯:

mainwindow.h

private slots:
    void    do_threadA_started();
    void    do_threadA_finished();

    void    do_newValue(int seq, int diceValue);
    void    do_newPicture(QString &picName);

mainwindow.cpp

    connect(threadA,&TDiceThread::started, this, &MainWindow::do_threadA_started);
    connect(threadA,&TDiceThread::finished,this, &MainWindow::do_threadA_finished);

    connect(threadValue,&TValueThread::newValue,this, &MainWindow::do_newValue);
    connect(threadPic,&TPictureThread::newPicture,this, &MainWindow::do_newPicture);

void MainWindow::do_threadA_started()
{//与线程的started()信号关联
    ui->statusbar->showMessage("Thread状态:thread started");
    ui->actThread_Run->setEnabled(false);
    ui->actThread_Quit->setEnabled(true);
}

void MainWindow::do_threadA_finished()
{//与线程的finished()信号关联
    ui->statusbar->showMessage("Thread状态:thread finished");
    ui->actThread_Run->setEnabled(true);
    ui->actThread_Quit->setEnabled(false);
}

void MainWindow::do_newValue(int seq, int diceValue)
{
    QString  str=QString::asprintf("第 %d 次掷骰子,点数为:%d",seq,diceValue);
    ui->plainTextEdit->appendPlainText(str);
}

void MainWindow::do_newPicture(QString &picName)
{
    QPixmap pic(picName);
    ui->labPic->setPixmap(pic);
}

另外为了避免线程还没结束就关闭程序,要重写一下wediget的closeevent函数:

void MainWindow::closeEvent(QCloseEvent *event)
{
    if (threadA->isRunning())
    {
        threadA->terminate();   //强制结束线程
        threadA->wait();        //等待线程结束
    }

    if (threadValue->isRunning())
    {
        threadValue->terminate();   //强制结束线程
        threadValue->wait();        //等待线程结束
    }

    if (threadPic->isRunning())
    {
        threadPic->terminate();   //强制结束线程
        threadPic->wait();        //等待线程结束
    }

    event->accept();
}

关闭窗口的时候检查三个线程是不是还在运行,如果在运行就强制关闭。 

这样程序大体就完成了,详见源程序 

标签:do,QT,threadA,void,互斥,线程,seq
From: https://blog.csdn.net/Excalibur6/article/details/144886141

相关文章

  • Windows编译QT6.4.3及使用
    1.下载QT6.4.3源码,并解压Indexof/archive/qt/6.4/6.4.3/singlehttps://download.qt.io/archive/qt/6.4/6.4.3/single/  2.安装环境*CMake3.18orlater*Perl5.8orlater*Python2.7orlater*C++compilersupportingtheC++17standard3.打开windows的cmd......
  • QT----------多媒体
    实现思路多媒体模块功能概述:QT的多媒体模块提供了丰富的功能,包括音频播放、录制、视频播放和摄像头操作等。播放音频:使用QMediaPlayer播放完整的音频文件。使用QSoundEffect播放简短的音效文件。录制音频:使用QMediaRecorder类进行音频录制。使用QAud......
  • QT-------------多线程
    实现思路QThread类简介:QThread是Qt中用于多线程编程的基础类。可以通过继承QThread并重写run()方法来创建自定义的线程逻辑。新线程的执行从run()开始,调用start()方法启动线程。掷骰子的多线程应用程序:创建一个DiceThread类继承自QThread,在run()......
  • C++11 thread线程的使用
    C++11thread线程的使用文章目录C++11thread线程的使用构造函数1.`thread()noexcept=default;`2.`thread(thread&)=delete;`3.`thread(constthread&&)=delete;`4.`thread(thread&&__t)noexcept`5.`template<typename_Callable,typename..._Args&g......
  • Java 线程相关的面试题
    Java线程相关的面试题是许多企业在考察Java并发编程能力时的重点内容。以下是一些常见的面试题及答案思路。1.什么是线程?线程和进程的区别是什么?线程是操作系统调度的最小单位,是进程中的一个执行单元。进程是资源分配的基本单位,每个进程拥有独立的内存空间。区别:线......
  • 统信UOS报错qt.qpa.plugin:Could not load the Qt platform plugin "xcb" in “***”
    问题截图: 解决方案:执行命令sudoln-fs/usr/lib/x86_64-linux-gnu/libxcb-util.so.0/usr/lib/x86_64-linux-gnu/libxcb-util.so.1 参考链接:Ubuntu22.04中解决CouldnotloadtheQtplatformplugin“xcb“问题解决方法_couldnotloadtheqtplatformplugin"xcb......
  • Python多线程与类方法的交互:锁提升安全性的奥秘
    目录一、Python多线程与类方法的交互案例1:多线程调用类方法二、为什么需要锁?案例2:使用锁来确保线程安全三、锁的工作原理四、锁的优缺点五、总结在Python编程中,多线程是一种提高程序运行效率的有效手段。特别是在处理I/O密集型任务时,多线程能够显著减少程序的等待时......
  • [Qt] 万字详解Qt入门~ Qt Creator | 对象树 | 控件布局
    目录1.QtCreator概览2.使用QtCreator新建项目3.认识QtCreator界面4.QtHelloWorld程序1.使用“按钮”实现2.使用“标签”实现3.使用“编辑框”实现5.项目文件解析1.命名空间声明与作用2.classWidget:publicQWidget6.Qt编程注意事项......
  • Python多线程使用
    在Python中,多线程是一种利用线程并发执行任务的技术,特别适合用于I/O密集型任务(如文件操作、网络请求等)。Python的多线程可以通过`threading`模块实现。以下是关于Python多线程的一些关键点和示例代码:---###**1.基本概念**-**线程**是一个轻量级的执行单元,与进程不同,多个......
  • worker子线程是否支持异步操作?
    Worker子线程支持异步操作。在前端开发中,Worker子线程(通常指WebWorker)被设计为在浏览器后台独立运行,以处理可能阻塞主线程的任务,从而保持页面的响应性。这种机制允许开发者将一些耗时的操作,如复杂的计算或数据处理,移至Worker子线程中执行,而不会干扰到主线程上的用户界面更新和用......