Qt学习笔记(四)多线程

系列文章目录

Qt开发笔记(一)Qt的基础知识及环境编译(泰山派)
Qt学习笔记(二)Qt 信号与槽
Qt学习笔记(三)网络编程
Qt学习笔记(四)多线程


文章目录

  • 系列文章目录
  • 前言
  • 一、QThead
    • 1.1 QThead的引入
    • 1.2 相关API
    • 1.3 QThread的工作流程
  • 二、继承QThread的代码实现


前言

  在Qt中,多线程的处理一般是通过QThread类来实现。QThread代表一个在应用程序中可以独立控制的线程,也可以和进程中的其他线程共享数据。之前我们在Linux应用篇中提到过多线程的概念,使用它的好处是可以同时操作好几个目标,而不是因为上一个目标未结束使得需要的操作陷入阻塞状态。


一、QThead

1.1 QThead的引入

  QThread 提供了线程启动、停止以及与其他对象通信的能力,我们可以利用主线程用于处理 GUI 操作,而长时间的耗时任务(如文件 I/O、网络请求、大数据处理等)可以放到其他线程中去执行,从而避免界面卡顿现象。QThread 是 Qt 提供的一个线程管理类,封装了原生的线程接口,使得线程的创建、启动、终止和通信更加直观和方便。
  QThread 线程类是实现多线程的核心类。Qt有两种多线程的方法,其中一种是继承QThread的run()函数,另外一种是把一个继承于QObject的类转移到一个Thread里。两种方法区别不大,用起来都比较方便,但继承QObject的方法更加灵活,笔者这里偏向前者。
继承QThread
  继承QThread是创建线程的一个普通方法。其中创建的线程只有run()方法在线程里的。其他类内定义的方法都在主线程内。
在这里插入图片描述
  通过上面的图我们可以看到,主线程内有很多方法在主线程内,但是子线程,只有 run()方法是在子线程里的。run()方法是继承于QThread类的方法,用户需要重写这个方法,一般是把耗时的操作写在这个run()方法里面。
继承QObject的线程
在这里插入图片描述
  与上一种方法不同,我们先写一个类继承 QObject,通过QObject::moveToThread()方法将它移到一个QThread线程里执行。那么可以通过主线程发送信号去调用QThread线程的方法如上图的fun4(),fun5()等等。这些方法都是在QThread线程里执行的。

1.2 相关API

函数名描述
run()线程入口函数
start()通过调用run()函数开始执行线程,操作系统根据优先级参数调度线程
currentThread()返回一个指向 管理当前执行线程 的QThread的指针
isRunning()若线程正在运行返回true,否则返回false
sleep()/msleep()/usleep()实现线程休眠,单位为秒/毫秒/微秒
wait()阻塞线程
quit()请求线程退出事件循环,常用于安全关闭线程。
finished()当线程结束时会发出该信号,可以通过该信号来实现线程的清理工作

注:使用wait()函数后,线程会阻塞至直到满足以下任何一个条件:

  • 与此QThread对象关联的线程已经完成执行(即当它从run()返回时),若线程已经完成,这个函数将返回true;若线程尚未启动,也返回true;
  • 已经过了几毫秒,若时间是ULONG_MAX(默认值),那么等待永远也不会超时(线程必须从run()返回),若等待超时,此函数返回false与POSIX pthread_join() 函数类似terminate()| 终止线程的执行。线程可以立即终止,也可以不立即终止,取决于操作系统的调度策略。在terminate()之后使用QThread::wait()来确保终止

1.3 QThread的工作流程

  就像前面提到的,笔者偏向继承QThread的写法,这里以这种为主。QThread 的核心流程是创建一个线程对象,将任务移动到该线程,然后启动线程以执行任务。QThread 内部维护了一个事件循环,确保线程可以响应事件和信号槽的触发。线程启动时自动调用 run() 方法,线程结束时会发送 finished 信号。

  1. 定义一个类继承 QThread 并重写 run() 函数
  2. 线程处理函数里面写入需要执行的复杂数据处理,需要注意以下几点:
     - 确定 run() 函数需要执行的具体数据处理任务,例如文件读取、数据分析、图像处理、网络请求等。
     - 处理流程设计:在 run() 中合理设计任务处理流程,比如是否需要循环、数据的预处理和后处理、错误处理等。
     - 线程间通信:在任务处理过程中,可能需要将进度、结果或错误状态传递回主线程。可以使用信号槽机制实现这些交互。
     - 资源释放:确保在任务完成时清理分配的资源,以防止内存泄漏或资源占用。
  3. 使用对象调用 start() 函数来启动线程
  4. 定义一个信号通知主线程执行完成
  5. 线程关闭与资源清理

二、继承QThread的代码实现

  代码部分也是利用前段时间写的毕设拯救计划(二)基于QT的智能家居(Onenet云)中的代码演示,它主要是在 Qt 中使用 QThread 可以让 DHT11 的数据读取在后台线程中执行,从而避免阻塞主界面线程。如果大家不会dht11的话可以看一下笔者之前写的驱动Linux驱动开发笔记(五) 基于设备树与GPIO子系统(含单总线)的操作实验。
  首先,新建一个类 DHT11ReaderThread,继承 QThread 并实现数据读取逻辑。

// dht11readerthread.h
#ifndef DHT11READERTHREAD_H
#define DHT11READERTHREAD_H#include <QThread>
#include <QString>class DHT11ReaderThread : public QThread
{Q_OBJECTpublic:DHT11ReaderThread(QObject *parent = nullptr);~DHT11ReaderThread();protected:// QThread 的主执行函数,前面提到了我们采用的方式主要是在run函数上void run() override; signals:// 用于发送新数据的信号void newData(QString temperature, QString humidity); private:// 用于控制线程的运行状态bool keepRunning; 
};#endif // DHT11READERTHREAD_H

  在 dht11readerthread.cpp的run函数中实现 DHT11 的数据读取逻辑,并在读取到数据后通过信号发送给主线程。

// dht11readerthread.cpp
#include "dht11readerthread.h"
#include "dht11.h"
#include <QDebug>DHT11ReaderThread::DHT11ReaderThread(QObject *parent): QThread(parent), keepRunning(true)
{dht11_init(); // 初始化 DHT11
}DHT11ReaderThread::~DHT11ReaderThread()
{keepRunning = false;dht11_close(); // 关闭 DHT11
}void DHT11ReaderThread::run()
{while (keepRunning) {char temperature;char humidity;// 读取 DHT11 数据if (dht11_read(&humidity, &temperature) == 0) {// 将读取到的数据格式化为字符串QString tempStr = QString("%1°C").arg((int)temperature);QString humiStr = QString("%1%").arg((int)humidity);// 发出信号,传递新数据emit newData(tempStr, humiStr);} else {qDebug() << "Failed to read data from DHT11.";}// 设置线程休眠 5 秒,控制读取频率msleep(5000);}
}

  在 MainWindow 的头文件中声明 updateDisplay 槽函数,用于接收来自线程的温湿度数据并更新 UI。

// mainwindow.h#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void updateDisplay(const QString &temperature, const QString &humidity); // 更新显示的槽函数private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H

  在 MainWindow 中创建 DHT11ReaderThread 的实例,并连接信号以更新 QTextBrowser。

// mainwindow.cpp#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "dht11readerthread.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);// 创建 DHT11 读取线程实例DHT11ReaderThread *readerThread = new DHT11ReaderThread(this);// 连接线程的信号到更新 QTextBrowser 的槽connect(readerThread, &DHT11ReaderThread::newData, this, &MainWindow::updateDisplay);// 启动线程readerThread->start();
}MainWindow::~MainWindow()
{delete ui;
}// 更新 QTextBrowser 显示的槽函数
void MainWindow::updateDisplay(const QString &temperature, const QString &humidity)
{QString displayText = QString("Temperature: %1\nHumidity: %2").arg(temperature).arg(humidity);ui->textBrowser->setText(displayText);
}

注:这里强调一下,不要在main.cpp和mainwindow.cpp中重复实例化对象。


免责声明:本文参考了网上公开的部分资料,仅供学习参考使用,若有侵权或勘误请联系笔者

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

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

相关文章

Elasticsearch 8.16:适用于生产的混合对话搜索和创新的向量数据量化,其性能优于乘积量化 (PQ)

作者&#xff1a;来自 Elastic Ranjana Devaji, Dana Juratoni Elasticsearch 8.16 引入了 BBQ&#xff08;Better Binary Quantization - 更好的二进制量化&#xff09;—— 一种压缩向量化数据的创新方法&#xff0c;其性能优于传统方法&#xff0c;例如乘积量化 (Product Qu…

【Python进阶】Python中的数据库交互:使用SQLite进行本地数据存储

1、数据持久化与访问效率 数据持久化是指程序运行过程中产生的数据能够长期保存&#xff0c;即使程序关闭或系统重启后仍可读取和修改。通过数据库&#xff0c;我们可以确保数据持久化的同时&#xff0c;实现数据的快速访问。例如&#xff0c;银行系统需要实时更新账户余额&am…

在vue中,在使用antdesign的table组件时,实现特定column列的显示与隐藏

我有这样一个columns列 const columns ref([{title: "权重",dataIndex: "weightiness",key: "weightiness",},{title: "名称",dataIndex: "name",key: "name",},{title: "属性",dataIndex: "attr…

Flume和kafka的整合

1、Kafka作为Source 【数据进入到kafka中&#xff0c;抽取出来】 在flume的conf文件夹下&#xff0c;有一个flumeconf 文件夹&#xff1a;这个文件夹是自己创建的 创建一个flume脚本文件&#xff1a; kafka-memory-logger.conf Flume 1.9用户手册中文版 — 可能是目前翻译最完…

现代密码学|古典密码学例题讲解|AES数学基础(GF(2^8)有限域上的运算问题)| AES加密算法

文章目录 古典密码凯撒密码和移位变换仿射变换例题多表代换例题 AES数学基础&#xff08;GF&#xff08;2^8&#xff09;有限域上的运算问题&#xff09;多项式表示法 | 加法 | 乘法X乘法模x的四次方1的乘法 AES加密算法初始变换字节代换行移位列混合轮密钥加子密钥&#xff08…

3. Spring Cloud Eureka 服务注册与发现(超详细说明及使用)

3. Spring Cloud Eureka 服务注册与发现(超详细说明及使用) 文章目录 3. Spring Cloud Eureka 服务注册与发现(超详细说明及使用)前言1. Spring Cloud Eureka 的概述1.1 服务治理概述1.2 服务注册与发现 2. 实践&#xff1a;创建单机 Eureka Server 注册中心2.1 需求说明 图解…

视频孪生技术在金融银行网点场景中的应用价值

作为国民经济重要的基础行业&#xff0c;金融行业在高速发展的同时衍生出业务纠纷、安全防范、职能管理等诸多问题&#xff0c;对安全防范和监督管理提出了更高的要求。因此&#xff0c;如何能更好的利用视频监控系统价值&#xff0c;让管理人员更简便的浏览监控视频、更快速的…

武汉EI学术会议一览表

武汉近期将举办BDDM2024大数据会议、ASIM2024智能制造会议、ICSGPS2025电网会议&#xff0c;吸引国内外学者参与&#xff0c;推动科技创新与产业发展&#xff0c;录用论文将提交EI索引。 武汉EI学术会议一览表 1.第二届大数据与数据挖掘国际会议&#xff08;BDDM 2024&#xf…

vue3项目中的常用插件

vue3项目中的常用插件 1、路由组件 npm install vue-router4路由配置 import { createWebHashHistory, createRouter } from "vue-router"; //引入主布局 import Admin from "/pages/layout/admin.vue"; import Index from "/pages/index/index.vu…

LeetCode Hot100 15.三数之和

题干&#xff1a; 思路&#xff1a; 首先想到的是哈希表&#xff0c;类似于两数之和的想法&#xff0c;共两层循环&#xff0c;将遍历到的第一个元素和第二个元素存入哈希表中&#xff0c;然后按条件找第三个元素&#xff0c;但是这道题有去重的要求&#xff0c;哈希表实现较为…

html + css 自适应首页布局案例

文章目录 前言一、组成二、代码1. css 样式2. body 内容3.全部整体 三、效果 前言 一个自适应的html布局 一、组成 整体居中&#xff0c;宽度1200px&#xff0c;小屏幕宽度100% 二、代码 1. css 样式 代码如下&#xff08;示例&#xff09;&#xff1a; <style>* {…

使用Axios函数库进行网络请求的使用指南

目录 前言1. 什么是Axios2. Axios的引入方式2.1 通过CDN直接引入2.2 在模块化项目中引入 3. 使用Axios发送请求3.1 GET请求3.2 POST请求 4. Axios请求方式别名5. 使用Axios创建实例5.1 创建Axios实例5.2 使用实例发送请求 6. 使用async/await简化异步请求6.1 获取所有文章数据6…

多仓库分支同步策略:方法与工具全解析

在大型项目或微服务架构中&#xff0c;多个仓库之间保持一致的分支结构是至关重要的。本文将为您介绍几种高效的方法和工具&#xff0c;帮助您实现这一目标。 方法一&#xff1a;Git Hooks——自动化同步的利器 Git Hooks允许您在特定事件&#xff08;如提交、合并等&#xf…

MySQL —— MySQL索引介绍、索引数据结构、聚集索引和辅助索引、索引覆盖

文章目录 索引概念索引分类索引数据结构种类Innodb 索引数据结构聚集索引和辅助索引&#xff08;非聚集索引&#xff09;聚集索引辅助索引&#xff08;非聚集索引&#xff09; 索引覆盖 索引概念 索引是对数据库表中一列或多列的值进行排序后的一种数据结构。用于帮助 mysql 提…

证明存在常数c, C > 0,使得在一系列特定条件下,某个特定投资时刻出现的概率与天数的对数成反比

在第0天&#xff0c;某债券价值1元。在第 n n n天&#xff0c;其价值为 S n : e ( X 1 ⋯ X n ) S_n : e^{(X_1 \cdots X_n)} Sn​:e(X1​⋯Xn​) 元&#xff0c;其中 X i X_i Xi​ 是独立同分布随机变量&#xff0c;满足 P ( X i 1 ) P ( X i − 1 ) 1 / 2 P(X_i 1)…

Vue.js中computed的使用方法

在Vue.js中&#xff0c;computed 属性是基于它们的依赖进行缓存的响应式属性。只有当相关依赖发生改变时&#xff0c;才会重新求值。这意味着只要computed属性依赖的源数据&#xff08;如data中的属性&#xff09;没有发生变化&#xff0c;多次访问computed属性会立即返回之前的…

制作图片马常用的五种方法总结

目录 1. 以文本方式2. Windows的cmd方式3. PhotoShop方式4. 16进制5. Linux的cat方式 图片马:就是在图片中隐藏一句话木马。利用.htaccess等解析图片为PHP或者asp文件。达到执行图片内代码目的。 1. 以文本方式 用文本方式&#xff08;这里用notepad。如果用记事本的方式打开…

python isinstance(True, int)

今天的bug 是布尔类型给的。 >>> a True >>> isinstance(a, int) True>>> a True >>> isinstance(a, bool) True‌Python中的布尔类型&#xff08;bool&#xff09;实际上是整数类型&#xff08;int&#xff09;的一个子类&#xff0c;…

python实现十进制转换二进制,tkinter界面

目录 需求 效果 代码实现 代码解释 需求 python实现十进制转换二进制 效果 代码实现 import tkinter as tk from tkinter import messageboxdef convert_to_binary():try:# 获取输入框中的十进制数decimal_number int(entry.get())# 转换为二进制binary_number bin(de…

UG Motion学习笔记2【正解 反解】

使用软件&#xff1a;SP Model NX12.0 Robot Arm的正解&#xff1a; &#xff08;先添加关节驱动&#xff0c;进行正解。再添加连杆驱动&#xff0c;进行反解。&#xff09; 直接点击_step.prt零件打开。一共7个构件&#xff0c;6个运动副&#xff08;圆片&#xff0c;转角&…