【Qt】QThread moveTothread-多线程的两种实现方法

一、如何理解多线程
二、实现多线程的两种方式(面向应用)
2.1 继承 QThread 的类
2.2 (推荐这种方式)函数 moveTothread()
三、多线程的释放问题(善后工作)

多线程的两种实现方法

  • 一、如何理解多线程
  • 二、实现多线程的两种方式(面向应用)
    • 2.1 继承 QThread 的类
    • 2.2 (推荐这种方式)函数 moveTothread()
  • 三、多线程的释放问题(善后工作)

一、如何理解多线程

类似我们单片机的编程,如在 Keil5 中对 51 单片机或者 STM32 单片机进行编程时,如果我们使用模块化编程,那么 main.c文件中可能代码如下:

#include "stm32f103c8t6.h"

void main(){
	//初始化配置函数
    while(1){
    	//控制函数,如点灯?
    }
}

从代码块中可以看出,我们写了一个 while 循环,目的是为了让程序一直运行,而如果去掉 while 循环,只点灯,可能灯就只会亮 1 下(可能人眼都看不到这一下)。而我们还会用到中断,中断就是打断主程序运行,去处理一下突然的内容(比如说外卖员给你打电话了,虽然你现在正在写代码,但是你接到了这个电话就去拿外卖了)
但是对于现在的电脑来说,中断是一种穿插执行任务的方式,多线程也是,这个可以理解为,我一边看动漫一边写代码,同时处理 2 件或以上的事情。

总结:多线程就是使程序能够同时处理多个任务
主线程:理解为窗口线程UI 线程默认线程,负责窗口事件处理或者窗口控件数据的更新
子线程:负责后台处理一些内容,不能对窗口对象做任何操作,这些事情需要交给窗口线程处理,如果主线程需要与子线程进行信息的交互,需要用到 Qt 中的信号槽机制(子线程将信号发给主线程,然后主线程再进行调用)。
应用:当程序在处理一个内容时,用多余的资源处理其他力所能及的事
对串口调试助手来说,若要开启多个串口,哪就是创建多个串口对象,然后同时运行即可?(2024年1月5日试一下)

二、实现多线程的两种方式(面向应用)

QThread的两种实现.zip(学习测试 moveTothread 和基础 QThread 两种方法,第一次写)
TreadMethod.zip(这篇博客的完整代码)

2.1 继承 QThread 的类

继承 QThread 的方式实现多线程,实际上这是一种理解上简单,但是在实际应用中稍显复杂,并且程序越复杂,这个方法越笨拙不灵活。(在看教程的时候看到网上的人说,如果要实现网络 TCP 调试助手的话,非常不建议用继承 QThread 的类的方式来实现多线程)
这种方法,实际上是继承 QThread 的类,然后通过通过重写 run 函数来实现多线程。接下来做一个小栗子:
实例要求:打印三个线程 ID,包括主线程(UI 线程)ID、子线程 run 函数线程ID、子线程槽函数线程ID
细节:程序运行时先打印子线程 ID 和主线程 ID,然后通过发出信号,触发子线程槽函数线程 ID。
Step1:新建一个类 thread_1,选择继承 QObject,然后改成继承 QThread 的类
recording.gif
Step2:重写 run 方法
Step3:写一个子线程的槽函数

#ifndef THREAD_1_H
#define THREAD_1_H
#include <QThread>
#include <QDebug>
class thread_1 : public QThread
{
Q_OBJECT
public:
explicit thread_1(QThread *parent = nullptr);
signals:
protected:
void run() override{
    qDebug()<<"子线程ID:" <<QThread::currentThreadId()<<'\n';
    sleep(5);
};
public slots:
void thread1Slot(){
    qDebug()<<"子线程槽函数ID:" <<QThread::currentThreadId()<<'\n';
}
};
#endif // THREAD_1_H

step4:在 UI 线程中打印线程 ID 和新建线程和初始化运行
step5:链接(主线程)信号和(子线程)信号槽,并发射信号触发子线程信号槽打印其目前的线程ID

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "thread_1.h"
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;
    thread_1* myThread;
signals:
    void mainwindownSignal();

};
#endif // MAINWINDOW_H

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //新建一个子线程对象并让他开始运行
    myThread = new thread_1;
    myThread->start();//开始运行后会执行run函数,打印子线程的ID
    //执行打印主函数线程ID
    qDebug()<<"主线程ID:" <<QThread::currentThreadId()<<'\n';
    //链接(主线程)信号和(子线程)信号槽,并发射信号触发子线程信号槽打印其目前的线程ID
    connect(this,&MainWindow::mainwindownSignal,myThread,&thread_1::thread1Slot);
    emit mainwindownSignal();
}

结果展示
① 结果让我有些意外,明明是让子线程先运行的,但是最先运行的是主线程,然后其次是子线程的槽函数,最后才是子线程的运行。
② 另外从图中可以看到,子线程的槽函数竟然处于主线程中!
image.png

2.2 (推荐这种方式)函数 moveTothread()

创建一个继承 QObject 的类(必须是继承 QObject),然后调用 moveTothread 方法,将这个继承 QObject 类的对象放到某个线程中,这样的好处是,可以让好几个对象放在同一个子线程中,而用方法一每次只能一个类来跑单一的线程,而且只能在 run 里跑,他子线程的槽函数还是运行在主线程中,想要实现多个线程就得继承多个 QThread 对象。
接下来做一个小栗子:
实例要求:打印三个线程 ID,包括主线程(UI 线程)ID、子线程 run 函数线程ID、子线程槽函数线程ID
细节:程序运行时先打印子线程 ID 和主线程 ID,然后通过发出信号,触发子线程槽函数线程 ID。
step1:创建一个继承 QObject 的类 work(与方法一类似,不展示了,为什么叫 work?我觉得因为这是一个要做的事所以叫他 work
step2:在这个类中写普通方法和槽函数

#ifndef WORK_H
#define WORK_H
#include <QObject>
#include <QDebug>
#include <QThread>
class work : public QObject
{
    Q_OBJECT
public:
    explicit work(QObject *parent = nullptr);
    void Working(){//写普通方法
        qDebug()<<"子线程ID:" <<QThread::currentThreadId()<<'\n';
    }
signals:
public slots:
    void WorkingSlot(){//写槽函数
        qDebug()<<"子线程ID:" <<QThread::currentThreadId()<<'\n';
    }
};
#endif // WORK_H

step3:在 mainwindow.h 中创建 workQThread 的对象:workermyThread
step4:在 mainwindow.h 中写触发 worker 槽函数的信号mainwindownSignal

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "work.h"
#include <QDebug>
#include <QThread>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
//step3:在 mainwindow.h 中创建 work 和 QThread 的对象:worker 和myThread
    Ui::MainWindow *ui;
    work* worker;
    QThread* myThread;
signals:
//step4:在 mainwindow.h 中写触发 worker 槽函数的信号mainwindownSignal
    void mainwindownSignal();
};
#endif // MAINWINDOW_H

step5:在 mainwindow.c 中进行 moveTothread、start 以及信号槽的连接触发操作

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    worker = new work;
    myThread = new QThread;
    worker->moveToThread(myThread);//moveTothread
    myThread->start();//也要start才能运行
    //worker->Working();//让工人工作!看看是不是在子线程中处理!
    connect(this,&MainWindow::mainwindownSignal,worker,&work::WorkingSlot);
    connect(this,&MainWindow::mainwindownSignal,worker,&work::Working);
    emit mainwindownSignal();//发出信号,让worker中的槽函数开始执行看看是在哪个线程中执行

     qDebug()<<"主线程ID:" <<QThread::currentThreadId()<<'\n';
}

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

结果展示
① 通过信号的方式触发操作子线程让其运行是在子线程中运行的。(但是如果直接调用 working 方法,还是在主线程中,L13 去掉注释看看)
image.png

三、多线程的释放问题(善后工作)

QThread 对象不能挂载在对象树上进行自动释放,会出问题,手动释放比较好。
第二部分介绍了怎么实现,但是对于多线程的资源释放,这里也简单总结一下,原理我也不是很擅长说。

当子线程运行结束后,退出线程并回收线程空间

connect(mComThread,&QThread::finished, this,&QObject::deleteLater);


参考教程:
Qt 教程-爱编程的大丙
Qt 串口多线程 继承QThread_pyqt串口多线程-CSDN博客
QThread的用法-CSDN博客

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

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

相关文章

文件操作(二)

͟͟͞͞&#x1f3c0;前言上一篇我们加们讲了什么是文件&#xff0c;为什么使用文件&#xff0c;以及流的概念。我们继续接上一篇来继续讲解我们的文件操作&#xff0c;这一篇将会详细的讲如何对文件进行读写。 目录 &#x1f680;一.文件的顺序读写 1.fgetc和fputc 2.fget…

oracle—IMU机制

正常的情况下&#xff0c;当事务需要回滚块的时候&#xff0c;是去undo表空间找 现在是在sharepool中分一个IMUbuffer&#xff0c;将所有的回滚信息写入。直接就可以从中取。减少了物理IO 同时这个过程也产生redo&#xff0c;直接就是图中红色的&#xff0c;不防止崩溃 优点 1…

Java21 + SpringBoot3集成WebSocket

文章目录 前言相关技术简介什么是WebSocketWebSocket的原理WebSocket与HTTP协议的关系WebSocket优点WebSocket应用场景 实现方式1. 添加maven依赖2. 添加WebSocket配置类&#xff0c;定义ServerEndpointExporter Bean3. 定义WebSocket Endpoint4. 前端创建WebSocket对象 总结 前…

Java 树形结构数据生成导出excel文件V2

** >> 相对于V1版本&#xff0c;优化了代码逻辑&#xff0c;合理使用递归计算树数据的坐标 << ** 1、效果 2、使用方法 import com.alibaba.fastjson.JSONArray; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Workboo…

Shiro框架:Shiro登录认证流程源码解析

目录 1.用户登录认证流程 1.1 生成认证Token 1.2 用户登录认证 1.2.1 SecurityManager login流程解析 1.2.1.1 authenticate方法进行登录认证 1.2.1.1.1 单Realm认证 1.2.1.2 认证通过后创建登录用户对象 1.2.1.2.1 复制SubjectContext 1.2.1.2.2 对subjectContext设…

【如何在 GitHub上面找项目】【转载】

很多的小伙伴&#xff0c;经常会有这样的困惑&#xff0c;我看了很多技术的学习文档、书籍、甚至视频&#xff0c;我想动手实践&#xff0c;于是我打开了GitHub&#xff0c;想找个开源项目&#xff0c;进行学习&#xff0c;获取项目实战经验。这个时候很多小伙伴就会面临这样的…

【数据结构 | 直接选择排序】

直接选择排序 基本思路直接插入排序SelectSort 基本思路 直接插入排序&#xff08;StraightInsertionSort&#xff09;的基本操作是将一个记录插入到已经排好序的有序表中&#xff0c;从而得到一个新的、记录数增1的有序表。 我们可以同时从数组的头部和尾部同时进行排序工作…

Pandoc:markdown转word

简介&#xff1a;Pandoc是由John MacFarlane开发的标记语言转换工具&#xff0c;可实现不同标记语言间的格式转换&#xff0c;堪称该领域中的“瑞士军刀”。Pandoc使用Haskell语言编写&#xff0c;以命令行形式实现与用户的交互&#xff0c;可支持多种操作系统&#xff1b;Pand…

IP-Adapter:用于文本到图像扩散模型的文本兼容图像提示适配器

文章目录 一、IP-Adapter简介二、IP-Adapter与img2img的区分&#xff08;一&#xff09;结构上的区别&#xff08;二&#xff09;流程上的区别&#xff08;三&#xff09;输出上的区别&#xff08;四&#xff09;原理上的区别 三、IP-Adapter的网络架构&#xff08;一&#xff…

自定义C#类库(.dll文件)

环境配置 操作系统&#xff1a;Windows 10 开发工具&#xff1a;Visual Studio 2022 .Net桌面开发环境&#xff1a; 开发步骤 &#xff08;一&#xff09;创建C#类库项目 &#xff08;二&#xff09;配置项目名称和项目路径 &#xff08;三&#xff09;选择所使用的框架&a…

ES数据聚合

1.数据聚合 聚合&#xff08;aggregations&#xff09;可以让我们极其方便的实现对数据的统计、分析、运算。例如&#xff1a; 什么品牌的手机最受欢迎&#xff1f; 这些手机的平均价格、最高价格、最低价格&#xff1f; 这些手机每月的销售情况如何&#xff1f; 实现这些…

PDF 文档解除密码

PDF 文档解除密码 1. 文件 -> 文档属性 -> 安全 -> 文档限制摘要2. PDF365References 1. 文件 -> 文档属性 -> 安全 -> 文档限制摘要 密码保护《算法设计与分析基础_第3版.pdf》 2. PDF365 https://www.pdf365.cn/ 免费功能 -> PDF 去密码 开始去除 Re…

PVE虚拟机配置文件恢复

一、pve 创建的虚拟机的配置文件位置 在宿主机的 /etc/pve/qemu-server&#xff0c;这里有创建虚拟机的相关硬件信息。 rootpve1:/etc/pve/qemu-server# pwd /etc/pve/qemu-server二、故障现象 在命令行执行qm list不显示虚拟机&#xff0c;查看 宿主机的 /etc/pve/qemu-ser…

【算法】Java-二叉树的右视图(BFS、DFS两种解法)

题目要求&#xff1a; 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 示例 1: 输入: [1,2,3,null,5,null,4] 输出: [1,3,4]示例 2: 输入: [1,null,3] 输出: [1,3]示例 3: 输入…

mysql原理--undo日志2

1.概述 上一章我们主要唠叨了为什么需要 undo日志 &#xff0c;以及 INSERT 、 DELETE 、 UPDATE 这些会对数据做改动的语句都会产生什么类型的 undo日志 &#xff0c;还有不同类型的 undo日志 的具体格式是什么。本章会继续唠叨这些 undo日志 会被具体写到什么地方&#xff0c…

LabVIEW利用视频分析实现高效硬度测量

LabVIEW利用视频分析实现高效硬度测量 在材料硬度测量领域&#xff0c;自动化和高精度测试技术的需求不断上升。布氏硬度机的自动化测量系统&#xff0c;尤其是那些结合了LabVIEW视频识别和处理技术的系统&#xff0c;正日益成为行业的焦点。介绍一个使用LabVIEW软件和先进的视…

mysql-实战案例 (超详细版)

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现错误&am…

用通俗易懂的方式讲解:大模型 RAG 技术,从入门到精通

本文基于IVAN ILIN发布于Towards AI的博客[1]进行总结归纳&#xff0c;感谢原作者的精彩讲解。 检索增强生成&#xff08;Retrieval Augmented Generation&#xff0c;简称RAG&#xff09;为大型语言模型&#xff08;LLMs&#xff09;提供了从某些数据源检索到的信息&#xff0…

svn spring项目增量打包工具

svn spring项目增量打包工具 前提介绍 项目使用svn &#xff0c;打包方式为war包&#xff0c;开发工具ide 项目有时候更新功能只需要更新部分class和html文件&#xff0c;但是要每个都打包并不是很简单 听说idea有现成的插件可以实现这个功能&#xff0c;但是我没找到&…

PPT插件-大珩助手-保留原素材的位置和大小一键替换

保留原素材的位置和大小一键替换 若勾选了一键替换&#xff0c;对于从素材库插入的图形&#xff0c;可以使得它的位置、大小与幻灯片中选中的形状一致 软件介绍 PPT大珩助手是一款全新设计的Office PPT插件&#xff0c;它是一款功能强大且实用的PPT辅助工具&#xff0c;支持W…
最新文章