QT 中的多线程之 moveToThread

news2024/11/10 11:48:32

文章目录

  • 1. 概述
  • 2. 方法描述
  • 3. 代码:
  • 4. 运行结果及说明
  • 5. 注意事项
  • 6. 结语

1. 概述

​QThread 类提供了一个与平台无关的管理线程的方法。一个 QThread 对象管理一个线程。

QThread 的执行从 run() 函数的执行开始,在 Qt 自带的 QThread 类中,run() 函数通过调用 exec() 函数来启动事件循环机制,并且在线程内部处理 Qt 的事件。

在 Qt 中建立线程的主要目的就是为了用线程来处理那些耗时的后台操作,从而让主界面能及时响应用户的请求操作。QThread 的使用方法有如下两种:

  1. QObject::moveToThread()
  2. 继承 QThread 类

下面通过具体的方法描述和例子来介绍第一种方法,第二种方法在下一篇文章中介绍。

2. 方法描述

在要使用线程的 controller 类中,新建一个 QThread 的对象和 woker 类对象,使用 moveToThread() 方法将 worker 对象的事件循环全部交由 QThread 对象处理。

Worker* worker = new Worker ;                 //创建一个QObject对象
QThread* _workerThread = new QThread(this);   //创建一个新线程
worker->moveToThread(&_workerThread);        // 调用 moveToThread 将该任务交给 workThread

如果这个 Worker 对象有槽函数,那么这些槽函数就会在新线程中执行, 如下类图中 dowork() 槽函数,就是新的线程中执行。

 

在这里插入图片描述

 

3. 代码:

首先新建一个 work 类,该类重点在于其 doWork 槽函数,这个函数定义了线程需要做的工作,需要向其发送信号来触发槽函数。

//
// Worker.h
//
#ifndef DEL_WORKER_H
#define DEL_WORKER_H

#include <QObject>
#include <QDebug>
#include <QThread>

class Worker : public QObject 
{
Q_OBJECT

public:
    explicit Worker(QObject *parent = nullptr);

public slots:
    void doWork(int parameter);   // doWork 定义了线程要执行的操作

signals:
    void resultReady(const int result);  // 线程完成工作时发送的信号
};
#endif //DEL_WORKER_H

//
// Worker.cpp
//
#include "Worker.h"

Worker::Worker(QObject *parent) {
}

void Worker::doWork(int parameter) {
    qDebug() << "receive the execute signal" ;
    qDebug() << "\tCurrent thread ID: " << QThread::currentThreadId();
    qDebug() << "\tFinish the work and sent the result Ready signal\n" ; 
    emit resultReady(parameter);
}

然后定义一个 Controller 类,这个类中定义了一个 QThread 对象,用于处理 worker 对象的事件循环工作。

//
// Controller.h
//
#ifndef DEL_CONTROLLER_H
#define DEL_CONTROLLER_H

#include <QObject>
#include <QThread>
#include <QDebug>
#include "Worker.h"

// controller 用于 启动子线程 和 处理子线程执行的结果
class Controller : public QObject 
{
  Q_OBJECT

public:
    explicit Controller(QObject *parent = nullptr);

    ~Controller() override ;

public slots:
    static void handleResults(int result);  // 处理子线程执行的结果

signals:
    void operate(const int);  // 发送信号,触发线程
    
private:
    QThread*      _workerThread = nullptr;
};

#endif //DEL_CONTROLLER_H


//
// Controller.cpp
//
/* 在 构造函数中创建 worker 对象,并且将其事件循环全部交给_workerThread 对象来处理,最后启动该线程,然后触发其事件处理函数。*/
#include "Controller.h"

Controller::Controller(QObject *parent) : QObject(parent) {
    auto *worker = new Worker ;
	_workerThread= new QThread(this);
    // 调用 moveToThread 将该任务交给 workThread
    worker->moveToThread(&_workerThread);

    // operate 信号发射后启动线程工作
    connect(this, SIGNAL(operate(const int)), worker, SLOT(doWork(int)));

    // 该线程结束时销毁
    connect(&_workerThread, &QThread::finished, worker, &QObject::deleteLater);

    // 线程结束后发送信号,对结果进行处理
    connect(worker, SIGNAL(resultReady(int)), this, SLOT(handleResults(int)));

    // 启动线程
    _workerThread.start();

    // 发射信号,开始执行
    qDebug() << "emit the signal to execute!" ;
    qDebug() << "\tCurrent thread ID:" << QThread::currentThreadId() << '\n' ;
    emit operate(0);
}

// 析构函数中调用 quit() 函数结束线程
Controller::~Controller() {
    _workerThread.quit();
    _workerThread.wait();
}

void Controller::handleResults(const int result) {
    qDebug() << "receive the resultReady signal" ;
    qDebug() << "\tCurrent thread ID: " << QThread::currentThreadId() << '\n' ;
    qDebug() << "\tThe last result is: " << result ;
}

接下来就是 main 函数,主函数中我们新建一个 Controller 对象,开始执行:

#include <QApplication>
#include <QThread>
#include "Controller.h"

int main(int argc, char *argv[])
{
    qDebug() << "I am main Thread, my ID: " << QThread::currentThreadId() << "\n" ;

    QApplication a(argc, argv);

    Controller c ;

    return a.exec();
}

4. 运行结果及说明

在这里插入图片描述

main 函数中打印当前线程编号,即主线程的线程编号是 0x7f4078b2b740,在 Controller 的构造函数中继续打印当前线程编号,也是主线程编号,之后把 work 类的工作交给子线程后,给子线程发送信号,子线程收到了信号开始执行,其线程号为0x7f404fd3c700,执行结束后发送信号给 Controller 处理结果。

5. 注意事项

在使用moveToThread函数时,需要注意以下几点:

  1. 只有QObject对象可以使用moveToThread函数,其他对象不能使用。
  2. 一旦调用了moveToThread函数,这个对象的线程上下文就会改变,因此在调用该函数之后,这个对象所属的线程上下文不能再使用。
  3. 如果对象正在执行某个函数,而该函数又没有使用线程锁,那么在移动对象之后,该函数仍然会在原来的线程中执行。因此,在移动对象之前,需要确保该对象不处于执行状态。
  4. 如果一个QObject对象的子对象也需要移动到新线程中,那么这些子对象也必须调用moveToThread函数进行移动。

6. 结语

moveToThread 方法,是把我们需要的工作全部封装在一个类中,将每个任务定义为一个槽函数,再建立触发这些槽函数的信号,然后连接信号和槽,最后调用 moveToThread 方法将这个类交给一个 QThread 对象,再调用 QThread 的 start() 函数使其全权处理事件循环。于是,任何时候我们需要让子线程执行某个任务,只需要发出对应的信号就可以。

其优点是我们可以在一个 worker 类中定义很多个需要做的工作,然后触发信号,子线程就可以执行。相比于继承 QThread 方法,只能执行 run() 函数中的任务,moveToThread 的方法中一个线程可以做很多不同的工作,只要实现对应的槽函数,触发对应的信号即可。

参考: QT 中的多线程—moveToThread 篇

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

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

相关文章

GraalVM编译SpringBoot程序

GraalVM 提供了一个名为 “Native Image” 的工具&#xff0c;它能够将 Java 应用程序预编译成本机可执行文件。这种方法的优点是启动速度快&#xff0c;内存占用少&#xff0c;因为程序运行时不需要 JVM 和类加载。 然而这种方式也存在一些弊端&#xff0c;如预编译的 GraalV…

扩展ACL配置

扩展ACL配置 【实验目的】 掌握扩展ACL的配置。认识扩展ACL的作用。验证配置。 【实验拓扑】 实验拓扑如图1所示。 图1 实验拓扑 设备参数如表所示。 表1 设备参数表 设备 接口 IP地址 子网掩码 默认网关 R1 S0/3/0 192.168.1.1 255.255.255.252 N/A Fa0/0/0 …

操作系统:文件系统基础

文件系统基础 ​ 文件是以硬盘为载体的存储在计算机上的信息集合&#xff0c;文件可以是文档、图片、程序等。在系统运行时&#xff0c;计算机以进程为基本单位进行资源的调度和分配&#xff1b;而在用户的输入和输出中&#xff0c;则以文件为基本单位 文件控制块和索引节点 …

【Python基础练习100题--第二篇:文件篇】

前言 这些题都是在B站的练习题&#xff0c;链接在这 对于刚学python的新手来说十分的适合&#xff0c; 可以加强和巩固我们的基础。 嘿嘿 一起噶油吧&#xff01;&#x1f349; &#x1f349;1.对学生成绩排序 # 这里对字典进行排序&#xff0c;同事使用到了sorted函数 # 这…

stm32F103 WIFIESP8266模块连接阿里云平台

(43条消息) ESP8266 AT MQTT 透传指令接入阿里云物联网平台笔记&#xff1b;_安信可科技的博客-CSDN博客 这里这篇博客提到的固件是错误的 我用的固件是这个&#xff0c;刷固件之后&#xff0c;可以连上阿里云。 ATMQTTCLIENTID0,"ClienId"//clientId第二个参数注…

权限提升:烂土豆 || DLL 劫持.

权限提升&#xff1a;烂土豆 || DLL 劫持. 权限提升简称提权&#xff0c;由于操作系统都是多用户操作系统&#xff0c;用户之间都有权限控制&#xff0c;比如通过 Web 漏洞拿到的是 Web 进程的权限&#xff0c;往往 Web 服务都是以一个权限很低的账号启动的&#xff0c;因此通…

数字化转型:如何利用文件管理系统提高制造业的工作效率

对于制造行业的企业用户&#xff0c;在日常企业文件管理中会遇到怎样的问题呢&#xff1f;制造业又该该如何高效管理文件呢&#xff1f; 这些问题困扰着机械制造行业客户…… 1&#xff09;项目多、文件杂&#xff0c;统一管理不便 2&#xff09;文档资料在公司内部流转不畅 …

14-5-进程间通信-信号

一、信号的基本概述 1.什么是信号&#xff1f; &#xff08;1&#xff09;对于linux来说&#xff0c;信号是软中断。比如在终端输入ctrlc来中断正在运行的程序&#xff0c;原理是linux通过信号机制来停止一个程序。 &#xff08;2&#xff09;每一个信号都有一个名字和编号&…

Django项目页面样式如何“传给”客户端浏览器

前言 django项目在视图函数中借助render函数可以返回HTML页面&#xff0c;但是HTML页面中如果引入了外部CSS文件或者JavaScript文件在浏览器页面无法加载&#xff0c;因此就必须有一种方式能够将HTML文档中引入的外部文件能够在客户端浏览器上加载&#xff0c;这种方式就是配置…

QML状态与过渡(States and Transitions)

目录 一 状态&#xff08;States&#xff09; 一 过渡&#xff08;Transitions&#xff09; 通常我们将用户界面描述为一种状态。一个状态定义了一组属性的改变&#xff0c;并且会在一定的条件下被触发。另外在这些状态转化的过程中可以有一个过渡&#xff0c;定义了这些属性…

第二十章 中介者模式

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目…

SpringSecurity框架学习与使用

SpringSecurity框架学习与使用 SpringSecurity学习SpringSecurity入门SpringSecurity深入认证授权自定义授权失败页面权限注解SecuredPreAuthorizePostAuthorizePostFilterPreFilter 参考 SpringSecurity学习 SpringSecurity入门 引入相关的依赖&#xff0c;SpringBoot的版本…

IPsec中IKE与ISAKMP过程分析(快速模式-消息3)

IPsec中IKE与ISAKMP过程分析&#xff08;主模式-消息1&#xff09;_搞搞搞高傲的博客-CSDN博客 IPsec中IKE与ISAKMP过程分析&#xff08;主模式-消息2&#xff09;_搞搞搞高傲的博客-CSDN博客 IPsec中IKE与ISAKMP过程分析&#xff08;主模式-消息3&#xff09;_搞搞搞高傲的博客…

Spring - IoC

Spring - IoC Spring- IoC1)Spring简介1.1)什么是框架1.2)框架的作用1.3)Spring是什么1.4)Spring的体系结构1.5)Spring的发展历史1.6)Spring优势 2)IoC简介2.1)优质程序代码的制作原则2.2)耦合与内聚2.3)工厂模式发展史2.4)Spring发展历程2.5)IoC 3)入门案例3.1)案例环境说明3.…

synchronized用法加锁原理

目录 使用场景不同场景加锁对象结论验证实验实验1&#xff1a; synchronized 修饰方法&#xff0c;加锁对象是类实例&#xff0c;不同实例之间的锁互不影响实验2&#xff1a; synchronized 加在静态方法上&#xff0c;加锁对象是方法所在类&#xff0c;不同类实例之间相互影响实…

docker镜像导入导出

项目背景 1. docker pull 的时候比较慢的情况下&#xff0c; 之前已经下载过的话&#xff0c;可以直接导入使用 2. 有些服务器不允许上外网的情况下&#xff0c;可以先在自己电脑上下载好&#xff0c;再上传到服务器上进行安装 导出镜像 查看镜像 docker images 导出镜像 …

【文件系统】

目录 1 inode 2 软链接 3 硬链接 1 inode 当我们创建一个文件时&#xff0c;用带上 -i 选项可以查看文件的inode: 其中第一个选项就是文件的inode,除此之外另外几列表示的是&#xff1a; 模式 硬链接数 文件所有者 所属组 大小 最后修改时间文件名 ls -l读取存储在磁盘上的文…

windows编译安卓源码记录

环境 Windows10 vmware17 ubuntu22 ubuntu环境设置 装完ubuntu系统后拖拽复制文件进去验证vmtools安装情况&#xff0c;如果vmtools异常很麻烦&#xff0c;试了n多方法&#xff0c;最后还是重新安装系统解决&#xff0c; 如果ok的话&#xff0c;再继续下步骤&#xff0c;否…

【C++入门第四期】类和对象 ( 上 )

前言类的使用类的定义类的两种定义方式&#xff1a;成员变量名的定义建议 类的访问限定符类的作用域类的实列化如何计算类的大小结构体内存对齐规则 this指针this指针的特性 前言 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过…