QT开发--多线程

第十四章 多线程

        QThread 是 Qt 中实现多线程编程的核心类,提供跨平台线程管理。

使用 QThread 有两种方法:

       1、 继承 QThread:重写 run() 方法,实现线程的具体操作。Qt4.8 之前较常用。

       2、 使用 QObjectmoveToThread():创建一个继承 QObject 的工作对象,使用 moveToThread() 将其移动到一个 QThread 对象中运行。官方推荐。

//当你调用moveToThread()时,
//你指定的QObject及其所有子对象将会在与该QThread对象相关联的线程中执行它们的槽函数。
void QObject::moveToThread(QThread *thread);

14.1 继承 QThread 的线程

        继承QThread是创建线程的一种方法。

        在这种方法中,只有重写的run()方法运行在子线程中,其他在类内定义的方法仍然在主线程中执行。用户需重写run()方法,并将耗时操作置于其中。

        用 connect关联 QThread类的对象的信号和 QWidget的槽函数。

14.1.1 应用实例

    // 告诉线程启动QThread::start();// 告诉线程退出  QThread::quit();  // 阻塞等待线程真正结束  QThread::wait();  

        本例展示了如何通过继承QThread类来创建和使用线程。在MainWindow类中,我们使用了一个按钮来启动线程,并在线程完成后接收其发送的信号。

#ifndef MAINWINDOW_H  // 防止头文件重复包含  
#define MAINWINDOW_H  #include <QMainWindow>  // 引入QMainWindow类  
#include <QThread>     // 引入QThread类  
#include <QPushButton>  // 引入QPushButton类  // 前向声明WorkerThread类  
class WorkerThread;  class MainWindow : public QMainWindow {  Q_OBJECT  // 使用Qt的宏,支持信号槽机制  
public:  MainWindow(QWidget *parent = nullptr);  // 构造函数  ~MainWindow();  // 析构函数  private:  WorkerThread *workerThread;  // 工作线程对象指针  QPushButton *pushButton;     // 按钮对象指针  private slots:  void handleResults(const QString &result);  // 处理线程结果的槽函数  void pushButtonClicked();  // 按钮点击的槽函数  
};  // WorkerThread类继承自QThread  
class WorkerThread : public QThread {  Q_OBJECT  // 使用Qt的宏,支持信号槽机制  
public:  WorkerThread(QWidget *parent = nullptr) { Q_UNUSED(parent); }  // 构造函数,忽略parent参数  // 重写run方法,线程在此方法中执行耗时操作  void run() override {  QString result = "线程开启成功";  // 定义结果字符串  sleep(2);  // 模拟耗时操作,延时2秒  emit resultReady(result);  // 发射结果准备好的信号  /*使用emit关键字发射一个信号时,该信号会被发送给所有与这个信号相连接的槽(slots)*/}  signals:  // 声明一个信号,当线程完成工作时发射  void resultReady(const QString &s);  
};  #endif // MAINWINDOW_H  // 头文件结束标志
#include "mainwindow.h"  // 引入MainWindow头文件  // MainWindow构造函数,初始化UI和线程  
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {  // 创建并设置按钮  pushButton = new QPushButton("开启线程", this);  // 创建工作线程对象  workerThread = new WorkerThread(this);  // 连接工作线程的信号到MainWindow的槽函数  connect(workerThread, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));  // 连接按钮点击信号到MainWindow的槽函数  connect(pushButton, SIGNAL(clicked()), this, SLOT(pushButtonClicked()));  
}  // MainWindow析构函数,处理线程关闭  
MainWindow::~MainWindow() {  // 告诉线程退出  workerThread->quit();  // 阻塞等待线程真正结束  workerThread->wait();  
}  // 处理线程结果的槽函数  
void MainWindow::handleResults(const QString &result) {  // 使用qDebug输出线程结果  qDebug() << result;  
}  // 按钮点击的槽函数  
void MainWindow::pushButtonClicked() {  // 检查线程是否已在运行,如果没有则启动线程  if (!workerThread->isRunning()) {  workerThread->start();  }  
}

        按钮点击后,QThread类start()函数开始运行,run()函数执行,

        2s后发送  emit resultReady(result);信号,触发槽函数打印结果。

14.2 继承 QObject 的线程

        继承QObject类并使用 QObject::moveToThread()方法。

        继承QObject类的方法更为灵活,因为它允许将一个QObject对象转移到另一个线程中执行。

14.2.1 应用实例

        mainWindow窗口类有 Worker:QObject成员,QThread成员

        Worker中定义任务,使用 QObject::moveToThread(&qthread)来代替重写QThread的run方法,使得线程可以执行这个任务

#ifndef MAINWINDOW_H  
#define MAINWINDOW_H  #include <QMainWindow>  
#include <QThread>  
#include <QDebug>  
#include <QPushButton>  
#include <QMutexLocker>  
#include <QMutex>  // 前向声明Worker类  
class Worker;  class MainWindow : public QMainWindow {  Q_OBJECT  public:  MainWindow(QWidget *parent = nullptr);  // 构造函数  ~MainWindow();  // 析构函数  private:  QPushButton *pushButton1;  // 开始线程按钮  QPushButton *pushButton2;  // 打断线程按钮  QThread workerThread;      // 工作线程  Worker *worker;            // 工人类实例  private slots:  void pushButton1Clicked();  // 开启线程槽函数  void pushButton2Clicked();  // 打断线程槽函数  void handleResults(const QString &); // 处理工作结果槽函数  signals:  void startWork(const QString &); // 发送开始工作信号  
};  // Worker类,用于处理耗时任务  
class Worker : public QObject {  Q_OBJECT  private:  QMutex lock;        // 互斥锁,用于线程同步  bool isCanRun;      // 控制工作是否可以继续的标志位  public slots:  void doWork1(const QString &parameter) {  isCanRun = true;  // 使用互斥锁保护共享资源,并在条件不满足时退出循环  while (true) {  QMutexLocker locker(&lock);  if (!isCanRun) break;  }  QThread::sleep(2); // 模拟耗时操作,秒  // 发射工作结果信号  emit resultReady(parameter + " doWork1 函数");  emit resultReady("打断 doWork1 函数"); // 通知工作已完成(此处设计可能不合理,通常只需在工作结束时发射一次信号)  }  public:  void stopWork() {  qDebug() << "打断线程";  /*创建了一个 QMutexLocker 对象,并立即尝试锁定传递给它的 QMutex 对象(即 lock)。如果 lock 已经被另一个线程锁定,那么这行代码会阻塞当前线程,直到 lock 被释放为止。*/QMutexLocker locker(&lock);  isCanRun = false;  // 设置标志位,停止工作  }  signals:  void resultReady(const QString &result); // 工作结果信号  
};  #endif // MAINWINDOW_H
#include "mainwindow.h"  MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {  // 设置窗口和按钮的基本属性  this->setGeometry(0, 0, 800, 480);  pushButton1 = new QPushButton("开启线程", this);  pushButton2 = new QPushButton("打断线程", this);  pushButton1->setGeometry(300, 200, 80, 40);  pushButton2->setGeometry(400, 200, 80, 40);  // 实例化Worker对象,并将其移动到workerThread线程中  worker = new Worker;  worker->moveToThread(&workerThread);  // 连接信号和槽  connect(&workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));  connect(&workerThread, SIGNAL(finished()), &workerThread, SLOT(deleteLater()));  connect(this, SIGNAL(startWork(QString)), worker, SLOT(doWork1(QString)));  connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));  connect(pushButton1, SIGNAL(clicked()), this, SLOT(pushButton1Clicked()));  connect(pushButton2, SIGNAL(clicked()), this, SLOT(pushButton2Clicked()));  
}  MainWindow::~MainWindow() {  // 打断线程并等待其结束  worker->stopWork();  workerThread.quit();  workerThread.wait(2000); // 阻塞等待2秒  qDebug() << "线程结束" << endl;  
}  void MainWindow::pushButton1Clicked() {  if (!workerThread.isRunning()) {  workerThread.start();  }  emit startWork("正在运行");  
}  void MainWindow::pushButton2Clicked() {  if (workerThread.isRunning()) {  worker->stopWork();  }  
}  void MainWindow::handleResults(const QString &results) {  qDebug() << "线程的状态:" << results << endl;  
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

树莓派应用--AI项目实战篇来啦-17.YOLOv8目标检测-安全帽检测

1. YOLOv8介绍 YOLOv8是Ultralytics公司2023年推出的Yolo系列目标检测算法&#xff0c;可以用于图像分类、物体检测和实例分割等任务。YOLOv8作为YOLO系列算法的最新成员&#xff0c;在损失函数、Anchor机制、样本分配策略等方面进行了全面优化和创新。这些改进不仅提高了模型的…

深入理解Transformer的笔记记录(精简版本)NNLM → Word2Vec

文章的整体介绍顺序为: NNLM → Word2Vec → Seq2Seq → Seq2Seq with Attention → Transformer → Elmo → GPT → BERT 自然语言处理相关任务中要将自然语言交给机器学习中的算法来处理,通常需要将语言数学化,因为计算机机器只认数学符号。向量是人把自然界的东西抽象出…

iOS 14 自定义画中画悬浮窗 Custom AVPictureInPictureController 实现方案

iOS 14&#xff0c;基于 AVPictureInPictureController&#xff0c;实现自定义画中画&#xff0c;涵盖所有功能与难点。 市面上的各种悬浮钟和提词器的原理都是基于此。 Demo源码在文末。 使用 iOS 画中画的要求&#xff1a; 真机&#xff0c;不能使用模拟器&#xff1b;iO…

Android平台RTSP|RTMP播放器PK:VLC for Android还是SmartPlayer?

好多开发者&#xff0c;希望在Android端低延迟的播放RTMP或RTSP流&#xff0c;本文就目前市面上主流2个直播播放框架&#xff0c;做个简单的对比。 VLC for Android VLC for Android 是一款功能强大的多媒体播放器&#xff0c;具有以下特点和功能&#xff1a; 广泛的格式支持…

FFmpeg的简单使用【Windows】--- 简单的视频混合拼接

实现功能 点击【选择文件】按钮在弹出的对话框中选择多个视频&#xff0c;这些视频就是一会将要混剪的视频素材&#xff0c;点击【开始处理】按钮之后就会开始对视频进行处理&#xff0c;处理完毕之后会将处理后的文件路径返回&#xff0c;并在页面展示处理后的视频。 视频所…

MySQL-08.DDL-表结构操作-创建-案例

一.MySQL创建表的方式 1.首先根据需求文档定义出原型字段&#xff0c;即从需求文档中可以直接设计出来的字段 2.再在原型字段的基础上加上一些基础字段&#xff0c;构成整个表结构的设计 我们采用基于图形化界面的方式来创建表结构 二.案例 原型字段 各字段设计如下&…

JAVA就业笔记4——第二阶段(1)

课程须知 A类知识&#xff1a;工作和面试常用&#xff0c;代码必须要手敲&#xff0c;需要掌握。 B类知识&#xff1a;面试会问道&#xff0c;工作不常用&#xff0c;代码不需要手敲&#xff0c;理解能正确表达即可。 C类知识&#xff1a;工作和面试不常用&#xff0c;代码不…

Redis:分布式 - 主从复制

Redis&#xff1a;分布式 - 主从复制 概念配置主从模式info replicationslave-read-onlytcp-nodelay 命令slaveof 主从结构一主一从一主多从 主从复制流程数据同步命令全量同步部分同步实时同步 节点晋升 概念 Redis的最佳应用&#xff0c;还是要在分布式系统中。对于非分布式…

Dockerfile 详解

Dockerfile是自定义Docker镜像的一套规则&#xff0c;由多条指令构成&#xff0c;每条指令都会对应于Docker镜像中的每一层&#xff0c;因为Docker是分层存储的。以下是Dockerfile中各个参数的详解及演示解析&#xff1a; 1. FROM 功能&#xff1a;指定待扩展的父级镜像&#…

【Linux系列】写入文本到文件

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

智慧乡村可视化设计,让美丽的乡村更加魅力。

智慧乡村可视化设计为美丽的乡村注入了新的活力&#xff0c;使其更加魅力四射。 通过可视化设计&#xff0c;乡村的自然风光得以更生动地展现。高清的全景图像、实时的视频监控&#xff0c;让人们仿佛身临其境&#xff0c;感受乡村的青山绿水、田园风光。 古老的村落、宁静的…

关于int*的*号归属权问题

再根据函数指针定义&#xff1a;int (*int) (int a)。我们发现*和后面的标识符才是一体的 所以int *a,b;的写法更好&#xff0c;说明a是指针类型&#xff0c;b是int类型

Python_函数式编程(生成器、迭代器、动态性)

简单说&#xff1a;时间换空间&#xff01;想要得到庞大的数据&#xff0c;又想让它占用空间少&#xff0c;那就用生成器&#xff01;延迟计算&#xff01;需要的时候&#xff0c;再计算出数据&#xff01; 创建生成器的方式二(生成器函数)生成器函数&#xff1a; 如果一个函数…

算法修炼之路之位运算

目录 一:位运算符及一些常用结论总结 1.给一个数n&#xff0c;确定它的二进制表示中的第x位是0还是1(位数从右向左0开始增加) 2.将一个数n的二进制表示形式的第x位修改成1 3.将一个数n的二进制表示的第x位修改为0 4.提取一个数n的二进制表示中最右侧的1 5.干掉一个数n的…

『Mysql进阶』Mysql explain详解(五)

目录 Explain 介绍 Explain分析示例 explain中的列 1. id 列 2. select_type 列 3. table 列 4. partitions 列 5. type 列 6. possible_keys 列 7. key 列 8. key_len 列 9. ref 列 10. rows 列 11. filtered 列 12. Extra 列 Explain 介绍 EXPLAIN 语句提供有…

24/10/12算法笔记 VGG

VGG特点&#xff1a; 1.深度&#xff1a;非常深 2.卷积核采用3*3&#xff0c;使得网络能够捕捉到更细粒度的图像特征 3.全连接层&#xff1a;使用全连接层来分类 4.使用ReLU激活函数&#xff0c;有助于缓解梯度消失 5.在卷积层和池化层后&#xff0c;使用局部归一化&#…

7-I2C与AHT20温湿度传感器

I2C与AHT20温湿度传感器 嵌入式领域另一种常见的通信IIC通信&#xff0c;并用其与AHT20传感器进行交互&#xff0c;获取房间的温度与湿度。 I2C有一条用于传递数据的数据线称为SDA&#xff08;Serial Data&#xff09;&#xff0c;另一条是用于提供同步时钟脉冲的时钟线SCL&am…

oracle数据坏块处理(一)-通过rman备份修复

表有坏块时&#xff0c;全表查询会报错&#xff1a; 这时候如果有前面正常的rman备份&#xff0c;那么我们就可以通过rman备份直接对数据文件块做恢复 先对数据文件做个逻辑检查&#xff1a; RMAN> backup check logical VALIDATE DATAFILE EXB_DATA/exb/datafile/cuteinf…

使用 three.js和 shader 实现一个五星红旗 飘扬得着色器

使用 three.js和 shader 实现一个五星红旗 飘扬得着色器 源链接&#xff1a;https://threehub.cn/#/codeMirror?navigationThreeJS&classifyshader&idchinaFlag 国内站点预览&#xff1a;http://threehub.cn github地址: https://github.com/z2586300277/three-ce…

【算法思想·二叉树】用「遍历」思维解题 II

本文参考labuladongsuanfa笔记[【强化练习】用「遍历」思维解题 II | labuladong 的算法笔记] 如果让你在二叉树中的某些节点上做文章&#xff0c;一般来说也可以直接用遍历的思维模式。 270. 最接近的二叉搜索树值 | 力扣 | LeetCode | 给你二叉搜索树的根节点 root 和一个目…