【QT5】<重点> QT多线程

文章目录

前言

一、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个资源,不成功时,不阻塞线程。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux - 进程

一、什么是进程 首先&#xff0c;Linux是一个多用户多进程的操作系统&#xff0c;系统上可以同时运行多个进程。 进程的产生&#xff1a;①是在执行程序或者命令时产生的&#xff1b;②定时任务进程 进程的类型&#xff1a;前台进程/后台进程 前台进程&#xff1a;一个终端…

迭代器模式观察者模式

文章目录 1.引出迭代器模式1.展示院系结构2.传统方式 2.迭代器模式解决院系结构展示问题1.基本介绍2.原理类图3.类图4.代码实现1.Department.java 存储信息的对象2.College.java 被迭代的类型接口3.ComputerCollege.java 被迭代的具体实现类&#xff0c;存储数据并将其在创建迭…

契约锁电子签章平台 add 远程命令执行漏洞复现(XVE-2023-23720)

0x01 产品简介 契约锁电子签章平台是上海亘岩网络科技有限公司推出的一套数字签章解决方案。契约锁为中大型组织提供“数字身份、电子签章、印章管控以及数据存证服务”于一体的数字可信基础解决方案,可无缝集成各类系统,让其具有电子化签署的能力,实现组织全程数字化办公。通…

【LeetCode刷题】面试题 17.19. 消失的两个数字

1. 题目链接2. 题目描述3. 解题方法4. 代码 1. 题目链接 面试题 17.19. 消失的两个数字 2. 题目描述 3. 解题方法 例子假设&#xff1a; 数组A元素为 &#xff1a;1 &#xff0c;4&#xff0c;5 缺少的元素为&#xff1a;2&#xff0c; 3 那么所有整数就为1 ~ 5&#xff…

什么是无限铸币攻击?它是如何运作的?

一、无限铸币攻击解释 无限铸币攻击是指攻击者操纵合约代码不断铸造超出授权供应限制的新代币。 这种黑客行为在去中心化金融 (DeFi) 协议中最为常见。这种攻击通过创建无限数量的代币来损害加密货币或代币的完整性和价值。 例如&#xff0c;一名黑客利用了 Paid 网络的智能…

shadertoy-安装和使用

一、安装vscode 安装vscode流程 二、安装插件 1.安装glsl编辑插件 2.安装shader toy插件 三、创建glsl文件 test.glsl文件 float Grid(float size, vec2 fragCoord) {vec2 r fragCoord / size;vec2 grid abs(fract(r - 0.5) - 0.5) / fwidth(r);float line min(grid…

网络安全从入门到精通(特别篇I):应急响应案例

蓝队应急响应实战 1. 应急响应1. 应急响应 获取当前WEB环境的组成架构(语言,数据库,中间件,系统等) 分析思路: 1、利用时间节点筛选日志行为 2、利用已知对漏洞进行特征筛选 3、利用后门查杀进行筛选日志行为 #内容点: 应急响应: 1、抗拒绝服务攻击防范应对指南 2、勒…

【云原生】Kubernetes----证书过期处理办法

目录 引言 一、证书过期的问题与影响 二、解决方案 &#xff08;一&#xff09;查看证书剩余时间 &#xff08;二&#xff09;备份重要数据 &#xff08;三&#xff09;更新证书 &#xff08;四&#xff09;重启相关组件的pod 引言 随着云计算技术的飞速发展&#xff0…

IPFoxy代理IP:IPv4与IPv6性能与安全性对比

在使用IPFoxy静态代理IP的过程中&#xff0c;经常有小白朋友疑惑&#xff0c;IPv4与IPv6有何区别&#xff1f;他们在性能与安全上的差别如何&#xff0c;又该如何选择&#xff1f;在这篇博文中&#xff0c;我们将从各个方面为您科普这一区别&#xff0c;帮助您更好的选择。 一、…

[Linux] vi编辑器

命令模式&文本模式 命令模式就输入命令然后执行&#xff0c;文本模式就是系统把你的输入都当成写进文件里的字符 切换模式&#xff1a; 刚进入默认是命令模式&#xff0c;按: i I a A o O 进入文本模式&#xff0c; 通过他们进入文本模式有什么不同&#xff1f; 然后按esc进…

一个新的剪辑拼接图片和视频类APP在测试阶段需要测试内容,以iPhone APP为例:

1.UI参照原型图和设计稿 如有改动&#xff0c;需及时沟通 2.iPad转屏、不同iPhone和iPad机型测试 3.黑夜白天模式 2.各功能模块流程需要测试跑通 3.订阅支付模块 a. UI设计是否和设计稿一致 b.涉及订阅的位置都要测试 c.免费试用是否显示&#xff1b;试用结束后&#xff0c…

【论文阅读】-- MIRIA:用于时空交互数据原位可视化和分析的混合现实工具包

MIRIA: A Mixed Reality Toolkit for the In-Situ Visualization and Analysis of Spatio-Temporal Interaction Data 摘要1 引言2 背景及相关工作2.1 用户交互和运动数据分析2.2 沉浸式分析 3 时空用户交互分析3.1 现有系统的用例和分析3.2 要求 4 MIRIA 工具包4.1 一般概念4.…

人工智能—美国加利福尼亚州房价预测实战

引言 在当今快速发展的房地产市场中&#xff0c;房价预测已成为一个至关重要的领域。它不仅关系到投资者的决策&#xff0c;也直接影响到普通购房者的生活质量。特别是在美国加利福尼亚州&#xff0c;这个以其高房价和房地产市场的波动性而闻名的地方&#xff0c;准确的房价预…

React@16.x(34)动画(中)

目录 3&#xff0c;SwitchTransition3.1&#xff0c;原理3.1.2&#xff0c;key3.1.2&#xff0c;mode 3.2&#xff0c;举例3.3&#xff0c;结合 animate.css 4&#xff0c;TransitionGroup4.1&#xff0c;其他属性4.1.2&#xff0c;appear4.1.2&#xff0c;component4.1.3&…

JWT整合Gateway实现鉴权(RSA与公私密钥工具类)

一.业务流程 1.使用RSA生成公钥和私钥。私钥保存在授权中心&#xff0c;公钥保存在网关(gateway)和各个信任微服务中。 2.用户请求登录。 3.授权中心进行校验&#xff0c;通过后使用私钥对JWT进行签名加密。并将JWT返回给用户 4.用户携带JWT访问 5.gateway直接通过公钥解密JWT进…

深圳中小企业融资攻略,贷款方法大盘点!

中小企业融资这事&#xff0c;可不是一个简单的事情。资金对中小企业来说&#xff0c;就像血液对人体一样重要。企业发展离不开资金支持&#xff0c;特别是在今年这个环境下&#xff0c;政策对中小企业还挺友好的。今天讲解一下中小微企业常用的几种贷款方法。希望能让大家更明…

MySQL之复制(五)

复制 复制的原理 复制文件 3.master.info 这个文件用于保存备库连接到主库所需要的信息&#xff0c;格式为纯文本(每行一个值)&#xff0c;不同的MySQL版本&#xff0c;其记录的信息也可能不同。此文件不能删除&#xff0c;否则备库在重启后无法连接到主库。这个文件以文本的…

Java面试八股之myBatis中#{}和${}标识的区别是什么

myBatis中#{}和${}标识的区别是什么 MyBatis中的#{}和${}都是用来动态地向SQL语句中插入参数的&#xff0c;但它们之间存在几个关键的区别&#xff1a; 预编译与字符串替换&#xff1a; #{}是预编译处理的参数占位符。MyBatis会将#{}中的内容替换为一个预编译语句的参数标记…

[Linux] 其他命令

cat cat是用法极多的一个命令&#xff1a; 1.输入输出 如果不加任何修饰&#xff0c;只一个cat&#xff0c;它是&#xff1a; 标准输入设备&#xff08;键盘&#xff09;——》标准输出设备&#xff08;显示器&#xff09; 显示文件内容&#xff1a; cat filename 输入重定向&a…

在金仓数据库中导入sql文件,解决中文数据乱码问题

先确定数据库服务端编码方式是UTF8&#xff0c;如果不是&#xff0c;那就先解决这个问题。操作&#xff1a;当连接数据库之后&#xff0c;执行show server_encoding 用Notepad打开&#xff0c;目的&#xff1a;确定文件编码是UTF-8格式 在sql文件前面加上set NAMES utf8; …