QT实现多线程的方法

目录

一、继承QThread类

1)自定义线程类

2)使用自定义的子线程类

3)使用说明

二、利用QThread的消息循环

1)自定义执行类

2)启动子线程

3)方法说明 

三、使用线程池

四、方法比较


        QT多线程编程常见的有3种实现方法,一种是继承QThread类,一种是利用QThread的消息循环,还有一种是使用线程池。这三种方式在不同的场景下各有优势,下面对三种实现方法进行详细说明。

一、继承QThread类

        直接继承QTread类是进行多线程编程最常用的一种方式。我们只需要定义一个自己的类,并继承自QThread即可。具体实现如下所示。

1)自定义线程类

        首先我们在头文件中自定义一个类MyThread,在类中定义好构造函数、析构函数和run函数。run函数必须要重写,用于实现子线程需要完成的工作。当子线程启动时,将直接执行run函数中的代码,也只有run函数中的代码是在子线程中执行的。在我们自定义的MyThread类中,我们同样可以定义公共函数,比如setPara(),用于在启动子线程之前,为子线程对象设置参数信息。同时也可以定义信号,用于子线程向外传递参数。在类MyThread的对象中,虽然run函数中的代码运行在子线程中,但run函数依然可以访问对象中的各类成员。但当run函数访问对象中的公有成员时,若创建该子线程对象的线程也需要访问该公有成员,则可能会存在访问冲突的问题,这需要在具体实现时加以注意,避免多个线程同时访问同一内存。

       类MyThread的CPP文件实现不再赘述,与所有类的实现一样。

mythread.h头文件#ifndef MYTHREAD_H
#define MYTHREAD_H#include <QThread>
#include <QObject>class MyThread : public QThread
{Q_OBJECT
public:MyThread (QObject *parent = nullptr);~MyThread ();
protected:void run();//重写该函数,实现子线程需要完成的工作
private:int a,b,c;
public:void setPara(int,int,int);
signals:void sendValue(int);
};
#endif // MYTHREAD_H

2)使用自定义的子线程类

       使用继承自QThread的类也非常简单。首先在我们需要使用的地方,包含自定义类的头文件mythread.h,然后使用该类实例化一个对象,在启动线程前,可以调用该对象的公有函数设置一些参数,最后调用对象的start()函数启动线程。如果需要从子线程中传递出参数,则可以使用信号和槽将子线程对象的信号与当前的槽函数连接。

3)使用说明

        需要指出的是,调用对象的start()函数后,将默认自动执行run函数中的代码。run函数执行完成后,线程将自动退出,子线程中不会运行消息循环机制。你可以多次调用start()函数,前提是线程已经成功退出,否则run函数不会执行。因此,需要重启子线程前,你需要判断前一次启动的线程是否真的已经成功退出(if (!mythread.isRunning()) { thread.start(); } else { thread.wait(); })。你确实需要这么做,因为真正结束子线程是操作系统来控制的,run函数执行完成只代表它的工作已经完成,但子线程的资源未必立即释放。

       在下面的例子中,我们直接在main函数中定义了子线程类,并启动了子线程,也就是我们直接在程序的主线程中创建了一个子线程。那么这个子线程对象的所有资源其实都属于主线程,我们可以在主线程中通过子线程对象myThread访问到该对象的所有公有成员。对象myThread不属于子线程,属于子线程的资源是在run函数中定义的对象。

#include "mythread.h"
#include <QApplication>
int main(int argc, char *argv[])
{QApplication a(argc, argv);MyThread myThread;myThread.setPra(1,2,3);myThread.start();return a.exec();
}

二、利用QThread的消息循环

        有时候我们可能不能确定何时需要启动子线程,而是要根据任务需要由程序类决定是否需要使用子线程。在这种情况,我们可以利用QThread的消息循环机制来实现,具体如下是QT帮助文档提供的示例,下面对示例代码进行逐一解释。

1)自定义执行类

       首先我们定义了一个需要在子线程中执行的类,该类完全由用户自定义,继承自基类QObject。然后在类中实现我们需要执行的业务操作函数,该函数必须定义为共有的槽函数,因为它需要由外部连接触发。我们也同样可以定义信号,向外部传递信息。

2)启动子线程

        在需要使用子线程的地方,首先创建一个QThread对象workerThread,这就是一个基本的QThread对象,然后再创建一个前面自定义的执行类对象worker,随后调用对象worker的moveToThread()函数,将worker移入到workerThread中,再连接信号和槽,最后调用workerThread的start函数启动子线程。

  class Worker : public QObject//定义一个需要在子线程中运行的类{Q_OBJECTpublic slots:void doWork(const QString &parameter) {//在子线程中需要执行的函数QString result;emit resultReady(result);}signals:void resultReady(const QString &result);//对外发送的信号};//以下是如何使用子线程class Controller : public QObject{Q_OBJECTQThread workerThread;//创建一个QThread 对象public:Controller() {Worker *worker = new Worker;//创建一个需要在子线程中运行的对象worker->moveToThread(&workerThread);//将子线程中运行的对象移入到workerThread对象中connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);//线程结束后删除对象connect(this, &Controller::operate, worker, &Worker::doWork);//链接外部信号,执行子线程的工作connect(worker, &Worker::resultReady, this, &Controller::handleResults);workerThread.start();//启动子线程,默认启动消息循环机制}~Controller() {workerThread.quit();//退出子线程workerThread.wait();}public slots:void handleResults(const QString &);signals:void operate(const QString &);};

3)方法说明 

        从前面的示例代码中我们可以发现,执行类Worker就是一个普通的类,它没有任何与线程相关的特殊定义或设置。moveToThread()函数是关键,它是QObject基类的成员函数,因此所有继承他的类的对象都可以调用该函数。示例代码中3个信号和槽连接,前两个是必要的,最后一个根据需要可以使用。当然,如果执行类Worker中有多个槽函数实现不同的业务工作,我们可以在添加连接更多的信号和槽。

       子线程的启动我们直接调用了start()函数,这里的启动机制与前述有所不同,它将直接创建子线程,并在子线程中运行消息循环机制,而不是运行run函数,这里我们也没有实现run函数。子线程将一直存在,直至我们调用quit()函数,或者主程序结束。即使在主程序退出前,也建议调用quit()函数,如上述示例代码所示,以确保成功释放资源。

       由于子线程一直存在,并运行着自己独立的消息循环,当上述代码中第二个连接的信号触发后,Worker类的dowork()槽函数就会执行,而它就是在子线程中执行的,并不是在创建Worker类对象的线程中(如主线程)。需要指出的是,虽然Worker类的dowork()槽函数在子线程中执行,但是Worker类的对象worker资源同样不属于子线程,而是创建它的线程。子线程只负责运行代码,访问资源,但并不管理对象worker的资源。

三、使用线程池

        首先看一下官方介绍:
        QThreadPool管理和重新设置单个QThread对象,以帮助降低使用线程的程序中的线程创建成本。每个Qt应用程序都有一个全局QThreadPool对象,可以通过调用globalInstance()访问该对象。要使用QThreadPool线程,请子类QRunnable并实现run()虚拟函数。然后创建该类的一个对象并将其传递给QThreadPool::start()。

        QT提供的线程池,使QT的线程使用更加方便灵活。官方示例代码如下,我们只需要子类化QRunnable,定义执行类,然后重写run函数,再将执行类的对象传递给QThreadPool::globalInstance()->start(hello)即可。

         这里使用的是Qt应用程序的全局QThreadPool对象,我们当然也可以定义一个自己的QThreadPool *threadPool对象,便于对线程池一些参数的设置。比如我们可以设置线程池中最大的线程数:threadPool->setMaxThreadCount(20)。

  class HelloWorldTask : public QRunnable{void run() override{qDebug() << "Hello world from thread" << QThread::currentThread();}};HelloWorldTask *hello = new HelloWorldTask();QThreadPool::globalInstance()->start(hello);

四、方法比较

        QT提供的3中多线程实现方法可根据需要选择。对于第一种直接继承QThread类, 它可以适用于需要长时间持续存在的,或者有阻塞机制的业务中。比如我们要用QT实现一个网络数据接收套接字程序,我们需要持续监听端口,子线程需要阻塞,这时就可以使用这种方式。对于第二种QThread消息循环机制,在我们需要频繁执行相同业务,但又不能事先确定何时调用时可以使用,比如处理较大的网络数据包时,可能因为网络数据包很大,处理时间较长,我们不愿交给主线程去做,这时就可以通过这种方式让子线程来处理。当收到数据包后,利用信号和槽机制,调用子线程中的槽函数处理数据。第三种线程池的方式则更加灵活,通常在处理大批量任务时比较适用,比如在并行计算中。总体而言,任何子线程业务工作都可以通过上述三种方式来实现,其区别主要是程序的开发便捷度和资源的优化利用上。

        最后需要指出的是,在子线程中我们应慎重使用无限循环,否则必须要为子线程的退出设置终止条件。

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

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

相关文章

基于机器学习时序库pmdarima实现时序预测

目录 一、Pmdarima实现单变量序列预测1.1 核心功能与特性1.2 技术优势对比1.3 python案例1.3.1 时间序列交叉验证1.3.1.1 滚动交叉验证1.3.1.2 滑窗交叉验证 时间序列相关参考文章&#xff1a; 时间序列预测算法—ARIMA 基于VARMAX模型的多变量时序数据预测 基于机器学习时序库…

【论文笔记】Are Self-Attentions Effective for Time Series Forecasting? (NeurIPS 2024)

官方代码https://github.com/dongbeank/CATS Abstract 时间序列预测在多领域极为关键&#xff0c;Transformer 虽推进了该领域发展&#xff0c;但有效性尚存争议&#xff0c;有研究表明简单线性模型有时表现更优。本文聚焦于自注意力机制在时间序列预测中的作用&#xff0c;提…

Matlab机械手碰撞检测应用

本文包含三个部分&#xff1a; Matlab碰撞检测的实现URDF文件的制作机械手STL文件添加夹爪 一.Matlab碰撞检测的实现 首先上代码 %% 检测在结构环境中机器人是否与物体之间发生碰撞情况&#xff0c;如何避免&#xff1f; % https://www.mathworks.com/help/robotics/ug/che…

从零开始:使用Jenkins实现高效自动化部署

在这篇文章中我们将深入探讨如何通过Jenkins构建高效的自动化部署流水线&#xff0c;帮助团队实现从代码提交到生产环境部署的全流程自动化。无论你是Jenkins新手还是有一定经验的开发者&#xff0c;这篇文章都会为你提供实用的技巧和最佳实践&#xff0c;助你在项目部署中走得…

鸿蒙harmony 手势密码

1.效果图 2.设置手势页面代码 /*** 手势密码设置页面*/ Entry Component struct SettingGesturePage {/*** PatternLock组件控制器*/private patternLockController: PatternLockController new PatternLockController()/*** 用来保存提示文本信息*/State message: string …

【Unity3D】UGUI的anchoredPosition锚点坐标

本文直接以实战去理解锚点坐标&#xff0c;围绕着将一个UI移动到另一个UI位置的需求进行说明。 &#xff08;anchoredPosition&#xff09;UI锚点坐标&#xff0c;它是UI物体的中心点坐标&#xff0c;以UI物体锚点为中心的坐标系得来&#xff0c;UI锚点坐标受锚点(Anchors Min…

Mp4视频播放机无法播放视频-批量修改视频分辨率(帧宽、帧高)

背景 家人有一台夏新多功能 视频播放器(夏新多功能 视频播放器),用来播放广场舞。下载了一些广场舞视频, 只有部分视频可以播放,其他视频均无法播放,判断应该不是帧速率和数据速率的限制, 分析可能是播放器不支持帧高度大于720的视频。由于视频文件较多,需要借助视频编…

自动化xpath定位元素(附几款浏览器xpath插件)

在 Web 自动化测试、数据采集、前端调试中&#xff0c;XPath 仍然是不可或缺的技能。虽然 CSS 选择器越来越强大&#xff0c;但面对复杂 DOM 结构时&#xff0c;XPath 仍然更具灵活性。因此&#xff0c;掌握 XPath&#xff0c;不仅能提高自动化测试的稳定性&#xff0c;还能在爬…

ASP.NET Core 如何使用 C# 向端点发出 POST 请求

使用 C#&#xff0c;将 JSON POST 到 REST API 端点&#xff1b;如何从 REST API 接收 JSON 数据。 本文需要 ASP .NET Core&#xff0c;并兼容 .NET Core 3.1、.NET 6和.NET 8。 要从端点获取数据&#xff0c;请参阅本文。 使用 . 将 JSON 数据发布到端点非常容易HttpClien…

【AI学习】关于 DeepSeek-R1的几个流程图

遇见关于DeepSeek-R1的几个流程图&#xff0c;清晰易懂形象直观&#xff0c;记录于此。 流程图一 来自文章《Understanding Reasoning LLMs》&#xff0c; 文章链接&#xff1a;https://magazine.sebastianraschka.com/p/understanding-reasoning-llms?continueFlagaf07b1a0…

CSS 实现下拉菜单效果实例解析

1. 引言 在 Web 开发过程中&#xff0c;下拉菜单是一种常见且十分实用的交互组件。很多前端教程都提供过简单的下拉菜单示例&#xff0c;本文将以一个简洁的实例为出发点&#xff0c;从 HTML 结构、CSS 样式以及整体交互逻辑三个层面进行详细解析&#xff0c;帮助大家理解纯 C…

网络安全溯源 思路 网络安全原理

网络安全背景 网络就是实现不同主机之间的通讯。网络出现之初利用TCP/IP协议簇的相关协议概念&#xff0c;已经满足了互连两台主机之间可以进行通讯的目的&#xff0c;虽然看似简简单单几句话&#xff0c;就描述了网络概念与网络出现的目的&#xff0c;但是为了真正实现两台主机…

【Windows】PowerShell 缓存区大小调节

PowerShell 缓存区大小调节 方式1 打开powershell 窗口属性调节方式2&#xff0c;修改 PowerShell 配置文件 方式1 打开powershell 窗口属性调节 打开 CMD&#xff08;按 Win R&#xff0c;输入 cmd&#xff09;。右键标题栏 → 选择 属性&#xff08;Properties&#xff09;…

GitCode 助力 Easy-Es,革新 Elasticsearch 开发体验

项目仓库&#xff08;点击阅读原文链接可直达&#xff09; https://gitcode.com/dromara/easy-es 项目背景&#xff1a;填补 Elasticsearch ORM 框架空白 在 Java 开发领域&#xff0c;Excel 和 Elasticsearch 的代码编写难度一直名列前茅&#xff0c;尤其是 Elasticsearch&a…

Vue(7)

一.Vuex &#xff08;1&#xff09;概述 1.是什么 vuex是一个vue的状态管理工具&#xff0c;状态就是数据&#xff0c;可以帮助管理vue通用的数据&#xff08;多组件共享的数据&#xff09; 2.场景 ①某个状态在很多个组件来使用&#xff08;个人信息&#xff09; ②多个组…

如何把邮件批量导出到本地

最近遇到邮箱满了的问题&#xff0c;需要把邮件批量导出到本地&#xff0c;然后清空邮箱。 问题是这个邮箱的官网&#xff0c;没有批量导出按钮&#xff0c;比较麻烦&#xff1b;总不能一封一封下载到本地&#xff0c;上万的。 找到了一个好用的工具&#xff0c;Mozilla Thun…

渗透利器工具:Burp Suite 联动 XRAY 图形化工具.(主动扫描+被动扫描)

Burp Suite 联动 XRAY 图形化工具.&#xff08;主动扫描被动扫描&#xff09; Burp Suite 和 Xray 联合使用&#xff0c;能够将 Burp 的强大流量拦截与修改功能&#xff0c;与 Xray 的高效漏洞检测能力相结合&#xff0c;实现更全面、高效的网络安全测试&#xff0c;同时提升漏…

如何将3DMAX中的3D文件转换为AutoCAD中的2D图形?

大家好,今天我们来探讨一下如何将3DMAX中的3D文件转换为AutoCAD中的2D图形。无论是出于设计交流、施工准备还是其他实际需求,这种转换在工程设计领域都是一项非常实用的技能。接下来,我将为大家详细介绍几种实现这一转换的方法,帮助大家轻松跨越3D与2D设计之间的鸿沟。让我…

Git 分布式版本控制工具使用教程

1.关于Git 1.1 什么是Git Git是一款免费、开源的分布式版本控制工具&#xff0c;由Linux创始人Linus Torvalds于2005年开发。它被设计用来处理从很小到非常大的项目&#xff0c;速度和效率都非常高。Git允许多个开发者几乎同时处理同一个项目而不会互相干扰&#xff0c;并且在…

国产编辑器EverEdit - 迷你查找

1 迷你查找 1.1 应用场景 某些场景下&#xff0c;用户不希望调出复杂的查找对话框&#xff0c;此时可以使用迷你查找窗口。 1.2 使用方法 选择主菜单查找 -> 迷你查找&#xff0c;或使用快捷键Ctrl Alt F&#xff0c;会在右上角弹出迷你查找窗口&#xff0c;如下图所示…