【QT5】<重点> QT多线程

news2024/11/15 18:04:26

文章目录

前言

一、QThread创建多线程

二、QMutex基于互斥量的同步

三、QReadWriteLock线程同步

四、QWaitCondition线程同步

五、QSemaphore基于信号量的同步


前言

本篇记录学习QT多线程的知识,参考视频13.1QThread创建多线程程序_哔哩哔哩。若涉及版权问题,请联系作者删除!


一、QThread创建多线程

1. 使用步骤:

  • ①创建的类需要继承QThread类。
  • ②重写run函数,新建的线程会执行run函数。
  • ③线程开启:对象调用start方法,使线程执行run函数。
  • ④线程终止:
    • 对象调用terminate方法,提示线程不再执行run函数。
    • 对象调用wait方法,等待线程执行完毕。
  • ⑤线程销毁:动态申请new需要调用deleteLater方法销毁线程对象。(该函数可以放置于run函数内,当run函数执行完毕后就会销毁该线程对象,防止内存泄漏)注意:尽量少用静态申请栈空间的方式创建线程对象,因为很可能该对象销毁时线程仍在执行,就会报错。

2. 案例演示:创建一个类继承QThread,在重写的run方法中根据是否暂停来随机生成骰子值。

【1】实现效果:

【2】dicethread.h:

#ifndef DICETHREAD_H
#define DICETHREAD_H

#include <QThread>

class DiceThread : public QThread
{
    Q_OBJECT

public:
    DiceThread();
    ~DiceThread();

private:
    void run() override;

signals:
    void diceGenerate(int diceCount, int diceValue);

private slots:
    void stopDiceThread_slot();
    void startGenerate_slot();
    void pauseGenerate_slot();

private:
    int diceCount = 0;  //第几次生成骰子
    int diceValue = 1;  //骰子的值
    bool pauseFlag = true;  //暂停生成骰子
    bool stopFlag = true;   //线程结束标志
};

#endif // DICETHREAD_H

【3】dicethread.cpp:

#include "dicethread.h"
#include <QRandomGenerator>

DiceThread::DiceThread()
{

}

DiceThread::~DiceThread()
{

}

/* 线程执行函数 */
void DiceThread::run()
{
    stopFlag = false;
    while (!stopFlag) {
        if (!pauseFlag) {
            diceValue = QRandomGenerator::global()->bounded(1, 7);
            emit diceGenerate(++diceCount, diceValue);
        }
        msleep(500);
    }
}

/* 接收到线程终止信号 */
void DiceThread::stopDiceThread_slot()
{
    stopFlag = true;
}

/* 接收到开始生成信号 */
void DiceThread::startGenerate_slot()
{
    pauseFlag = false;
}

/* 接收到停止生成信号 */
void DiceThread::pauseGenerate_slot()
{
    pauseFlag = true;
}

【4】widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "dicethread.h"

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

signals:
    void stopDiceThread();  //线程停止,DiceThread内部修改标志位
    void startGenerate();   //开始生成,DiceThread内部修改标志位
    void pauseGenerate();   //暂停生成,DiceThread内部修改标志位

private slots:
    void on_btnStartThread_clicked();
    void diceGenerate_slot(int, int);//接收DiceThread的骰子信息
    void on_btnStopThread_clicked();
    void on_btnDiceBegin_clicked();
    void on_btnDiceEnd_clicked();
    void on_btnClearText_clicked();

private:
    Ui::Widget *ui;
    DiceThread *dice = nullptr;
};
#endif // WIDGET_H

【5】widget.cpp:

#include "widget.h"
#include "ui_widget.h"
#include <QString>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //设置按钮互斥
    ui->btnStartThread->setEnabled(true);
    ui->btnStopThread->setEnabled(false);
    ui->btnDiceBegin->setEnabled(false);
    ui->btnDiceEnd->setEnabled(false);
}

Widget::~Widget()
{
    delete ui;
}

/* 槽函数:接收diceGenerate信号*/
void Widget::diceGenerate_slot(int diceCount, int diceValue)
{
    ui->plainTextEdit->appendPlainText("第" + QString::number(diceCount) + "次,生成点数为:"
                                       + QString::number(diceValue));
}

/* "启动线程"按钮 */
void Widget::on_btnStartThread_clicked()
{
    //创建DiceThread对象
    dice = new DiceThread;
    connect(dice, SIGNAL(diceGenerate(int, int)), this, SLOT(diceGenerate_slot(int, int)));
    connect(this, SIGNAL(stopDiceThread()), dice, SLOT(stopDiceThread_slot()));
    connect(this, SIGNAL(startGenerate()), dice, SLOT(startGenerate_slot()));
    connect(this, SIGNAL(pauseGenerate()), dice, SLOT(pauseGenerate_slot()));
    //启动线程
    dice->start();
    ui->plainTextEdit->appendPlainText("线程已启动");
    //设置按钮互斥
    ui->btnStartThread->setEnabled(false);
    ui->btnStopThread->setEnabled(true);
    ui->btnDiceBegin->setEnabled(true);
    ui->btnDiceEnd->setEnabled(false);
}

/* "停止线程"按钮 */
void Widget::on_btnStopThread_clicked()
{
    dice->terminate();
    dice->wait();
    dice->deleteLater();
    ui->plainTextEdit->appendPlainText("线程已停止");
    emit stopDiceThread();
    //设置按钮互斥
    ui->btnStartThread->setEnabled(true);
    ui->btnStopThread->setEnabled(false);
    ui->btnDiceBegin->setEnabled(false);
    ui->btnDiceEnd->setEnabled(false);
}

/* "开始"按钮 */
void Widget::on_btnDiceBegin_clicked()
{
    emit startGenerate();
    ui->btnDiceBegin->setEnabled(false);
    ui->btnDiceEnd->setEnabled(true);
}

/* "暂停"按钮 */
void Widget::on_btnDiceEnd_clicked()
{
    emit pauseGenerate();
    ui->btnDiceBegin->setEnabled(true);
    ui->btnDiceEnd->setEnabled(false);
}

/* "清空"按钮 */
void Widget::on_btnClearText_clicked()
{
    ui->plainTextEdit->clear();
}

二、QMutex基于互斥量的同步

1. 背景:当多个线程同时访问一块内存区域时,就会出现错误,因此需要线程同步。

2. QMutex:在互斥量之前上锁,然后在一个线程使用完互斥量之后解锁。如果一个线程试图向一个已经被其它线程上锁的互斥量上锁的话,这个线程将被阻塞,直到这个互斥量被解锁。

3. 基本使用:

  • lock():上锁
  • unlock():解锁
  • tryLock():尝试上锁,若成功则上锁,不成功则返回false

4. 案例演示: 

创建两个线程来同时访问value,通过查看相关调试信息来获取是哪个线程修改了value的值。我们是想让两个线程交叉着访问value。

【1】不加锁的代码:

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>

class MyThread : public QThread
{
    Q_OBJECT

public:
    MyThread();
    void run() override;
    void setNum(int *num);

private:
    int *num;
};

#endif // MYTHREAD_H
#include "mythread.h"

MyThread::MyThread()
{

}

void MyThread::run()
{
    while (*num > 0) {
        (*num)--;
        qDebug("线程号: %d, value = %d", QThread::currentThreadId(), *num);
    }
}

void MyThread::setNum(int *num)
{
    this->num = num;
}

#include "widget.h"
#include <QApplication>
#include "mythread.h"

int main(int argc, char *argv[])
{
    int value = 20;
    MyThread thread1, thread2, thread3;
    thread1.setNum(&value);
    thread2.setNum(&value);
    thread3.setNum(&value);
    thread1.start();
    thread2.start();
    thread3.start();
    while (1) {}
    return 0;
}

从运行结果中,我们能够看出value递减打印出现问题,这不是我们想要的

【2】使用QMutex后:就不会出现上述的问题。


三、QReadWriteLock线程同步

1. 说明:使用互斥量时存在一个问题,每次只能有一个线程获得互斥量的权限。如果多个线程读取某个变量,就会出现排队现象。而实际上,有需求让多个线程同时读取。因此,引入QReadWriteLock实现线程同步。

2. 主要函数:

  • lockForRead():只读方式锁定资源,如果有其他线程以写入方式锁定,这个函数会阻塞。
  • lockForWrite():以写入方式锁定资源,如果其他线程以读或写模式锁定资源,则函数堵塞。
  • unlock():解锁。
  • tryLockForRead():是lockForRead的非阻塞版本。
  • tryLockForWrite():是lockForWrite的非阻塞版本。

四、QWaitCondition线程同步

1. 说明:在著名的“生产者-消费者”模型中,生产者负责制作蛋糕,消费者负责拿走蛋糕。此时,生产者制作蛋糕完成后应该通知消费者,因此引入QWaiteCondition实现线程同步,需要搭配QMutex锁。

2. 常用函数:

  • wait(QMutex *lockedMutex):进入等待状态,解锁互斥量lockedMutex,被唤醒后锁定lockedMutex并退出函数。
  • wakeAll():唤醒所有处于等待状态的线程,唤醒顺序不确定。
  • wakeOne():唤醒一个处于等待状态的线程,唤醒哪个不确定。

五、QSemaphore基于信号量的同步

1. 说明:信号量和互斥量的区别在于,信号量可以被多个线程同时访问,而互斥量只能被一个线程访问。同时,在“生产者-消费者”模型中,可能会有多个生产者生产蛋糕,生产者最多可以容纳10个蛋糕,然后消费者在蛋糕生产出来后就可以拿走。因此,引入QSemaphore实现基于信号量的线程同步。

2. 注意:

  • QSemaphore 类中的信号量实际上是一个计数器,它用于记录能够同时访问某个共享资源的线程数。

3. 常用函数:

  • acquire(int n):尝试获得n个资源,如果不够将堵塞线程,直到n个资源可用。调用后,信号量计数器-1.
  • release(int n):释放资源,如果资源已经全部可用,则可扩充资源总数。调用后,信号量计数器+1.
  • int available():返回当前信号量的资源个数。
  • bool tryAcquire(int n=1):尝试获取n个资源,不成功时,不阻塞线程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1839831.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C语言】C语言入门宝典:核心概念全解析

. C语言专栏 | C专栏 &#x1f449; 个人主页 &#x1f448; 前言 此篇文章我们主要是宏观的了解一下什么是C语言&#xff0c;C语言里面有那些知识点&#xff0c;所有的知识点我们此篇只是以入门为主&#xff0c;点到为止&#xff0c;简单易懂&#xff0c;后期的文章会一 一详…

苍穹外卖---编辑员工(P27-P29)

一、需求分析与设计 &#xff08;1&#xff09;产品原型 在员工管理列表页面点击 "编辑" 按钮&#xff0c;跳转到编辑页面&#xff0c;在编辑页面回显员工信息并进行修改&#xff0c;最后点击 "保存" 按钮完成编辑操作。 员工列表原型&#xff1a; 修改…

全网最全!25届最近5年华东理工大学自动化考研院校分析

华东理工大学 目录 一、学校学院专业简介 二、考试科目指定教材 三、近5年考研分数情况 四、近5年招生录取情况 五、最新一年分数段图表 六、历年真题PDF 七、初试大纲复试大纲 八、学费&奖学金&就业方向 一、学校学院专业简介 二、考试科目指定教材 1、考试…

Google 广告VS Facebook广告:哪个更适合我?2024全维度区别详解

在 Google Ads 和 Facebook Ads 之间进行选择可能是一个艰难的决定。决定哪种方法适合您的业务取决于多种因素&#xff0c;从您愿意为转化支付的费用到管理广告系列所需的时间和人员。在这篇文章中&#xff0c;将解释 Google Ads 和 Facebook Ads 之间的差异&#xff0c;以便您…

吴恩达机器学习作业ex3:多类分类和前馈神经网络(Python实现)详细注释

文章目录 1 多类分类1.1数据集1.2 数据可视化1.3 向量化逻辑回归1.3.1 向量化代价函数1.3.2 矢量化梯度下降以及正则化表达1.4 一对多分类 2.神经网络2.1模型表示 总结&#xff08;自己训练求解参数全流程&#xff09; 1 多类分类 在本练习中&#xff0c;您将使用逻辑回归和神…

element v-loading 加载组件增加按钮

2024.06.19今天我学习了如何对v-loading组件增加按钮&#xff0c;效果如下&#xff1a; 代码如下&#xff1a; <template><el-table v-loading"loading"><template v-slot:append><el-button v-if"loading" type"warning"…

龙芯LS2K0300久久派上手体验

介绍 芯片 龙芯2K0300芯片是一款基于LA264处理器核的多功能SoC芯片&#xff0c;可广泛适用于工业控制、通信设备、信息家电和物联网等领域&#xff1b;该芯片采用高集成度设计&#xff0c;可提供丰富的功能接口&#xff0c;满足多场景应用需求&#xff0c;同时支持低功耗技术…

springboot中,将某个函数的日志单独输出的方法

这里写自定义目录标题 背景解决方案解决过程原理 背景 项目中有个节点健康检查扫描功能&#xff0c;每10秒扫描一次节点。 如果节点挂掉&#xff0c;会输出健康检查失败的日志。 测试环境&#xff0c;虽然配置了多个节点&#xff0c;但并没有都启动&#xff0c;所以在扫描的时…

Python学习笔记14:进阶篇(三)。类的终结篇,类的导入和模块的导入。

前言 这篇文章属于类知识的最后一篇&#xff0c;带一点点其他知识&#xff0c;学习内容来自于Python crash course。 关注我私信发送Python crash course&#xff0c;分享一份中文版PDF。 类的导入 在学习的时候&#xff0c;包括之前&#xff0c;我都是在一个文件中把所有代…

matlab线性多部法求常微分方程数值解

用Adamas内差二步方法&#xff0c;内差三步方法&#xff0c;外差二步方法&#xff0c;外差三步方法这四种方法计算。 中k为1和2. k为2和3 代码 function chap1_adams_methodu0 1; T 2; h 0.1; N T/h; t 0:h:T; solu exact1(t);f f1; u_inter_2s adams_inter_2steps(…

【C++高阶】高效搜索的秘密:深入解析搜索二叉树

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;C多态 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀二叉搜索树 &#x1f4d2;1. 二叉搜索树&…

项目训练营第二天

项目训练营第二天 用户登录逻辑 1、账户名不少于4位 2、密码不少于8位 3、数据库表中能够查询到账户、密码 4、密码查询时用同样加密脱敏处理手段处理后再和数据库中取出字段进行对比&#xff0c;如果账户名未查询到&#xff0c;直接返回null 5、后端设置相应的脱敏后用户的s…

如何使用alias永久别名(linux篇)

一、alias的使用 alias主要作用是起一个别名的用处 它又分两种形式&#xff1a; ① 临时别名 ② 永久别名 1.第一种&#xff08;临时别名&#xff09;&#xff1a; C:\Users\62452>ssh root192.168.0.102 root192.168.0.102s password: Last login: Sat Jun 15 16:30:12 20…

18张Python数据科学速查表.png

数据科学已经发展成为一个庞大的系统&#xff0c;包含数学、统计学、概率论、计算机、数据库、编程等各种理论技术。 目前在主流的数据科学领域一般有三大生态&#xff0c;一是以sas、matlab、spss等为代表的商业软件生态&#xff0c;二是围绕R语言建立起来的开源生态&#xf…

移动硬盘在苹果电脑上无法识别的诊断与恢复策略

一、问题描述 在数字时代&#xff0c;移动硬盘已成为我们存储和传输数据的重要工具。然而&#xff0c;当我们将移动硬盘插入苹果电脑时&#xff0c;有时会遇到无法识别的情况&#xff0c;这让我们感到十分困扰。本文将详细探讨移动硬盘插苹果电脑后读不出来的现象&#xff0c;…

GIT----使用技巧之保存现场回退新建分支继续开发

GIT----使用技巧之保存现场回退新建分支继续开发 前言&#xff1a; 故事是这样的&#xff0c;有一个比较复杂的项目使用的是STM32F103VCT6&#xff08;资源flash-256k,RAM-48k&#xff09;,开发到一半发现RAM不够用了&#xff0c;换容量更大的芯片STM32F103VGT6&#xff08;资源…

Elasticsearch安装(windows)

先给出网址 elasticsearch&#xff1a;Download Elasticsearch | Elastic elasticKibana&#xff1a;Download Kibana Free | Get Started Now | Elastic Logstash&#xff1a;Download Logstash Free | Get Started Now | Elastic ik分词&#xff1a;Releases infinilabs/…

经验分享,16进制与字符串的互相转换网站

分享一个16进制与字符串的互相转换的网站&#xff0c;比较实用。 网址&#xff1a; https://www.bejson.com/convert/ox2str/ 截图&#xff1a;

pytorch自定义扩展

一 torch.nn.Module和torch.autograd.Function pytorch的自定义拓展之&#xff08;一&#xff09;——torch.nn.Module和torch.autograd.Function_用torch.autograd.function还是nn.module-CSDN博客https://blog.csdn.net/qq_27825451/article/details/95189376

人力资源招聘社会校企类型招聘系统校园招聘小程序

校企社会人力资源招聘小程序&#xff1a;开启高效招聘新时代 &#x1f680;开篇&#xff1a;打破传统&#xff0c;开启招聘新篇章 在快速发展的现代社会&#xff0c;人力资源招聘已经成为企业和学校共同关注的重要议题。为了更高效、便捷地满足双方的招聘需求&#xff0c;一款…