QThread 与QObject::moveToThread在UI中的应用

2024-06-22 03:28

本文主要是介绍QThread 与QObject::moveToThread在UI中的应用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. QThread的两种用法

第一种用法就是继承QThread,然后覆写 virtual void run(), 这种用法的缺点是不能利用信号槽机制。

第二种用法就是创建一个线程,创建一个对象,再将对象moveToThread, 这种可以充分利用信号槽机制,与UI框架完美融合。这与std::thread也是不一样的地方。

2. moveToThread用法讲解

示例地址:MultiThread/QThreadDemo · 沁明/QtDemo - 码云 - 开源中国 (gitee.com)

直接调用 QObject::moveToThread() 函数,将继承自 QObject 的对象移到线程里面。此时该对象的 槽函数运行在另一个线程里面。 也就是说,当有信号绑定到该对象的槽函数的时候,并发送信号,该槽函数就运行在另一个线程里,否则该函数仍然运行在对象所在的线程中。

1)先创建一个QObject的子类。

它要运行的函数都可以在子线程中运行。

worker.h

#ifndef WORKER_H
#define WORKER_H#include <QObject>class Worker : public QObject
{Q_OBJECT
public:explicit Worker(QObject *parent = nullptr);public slots:void workSlot();void msg1Slot(const QString &m1);void msg3Slot(const QString &m3);signals:void msg1(const QString &m1);void msg2(const QString &m2);
};#endif // WORKER_H

 worker.cpp

#include "worker.h"#include <QDebug>
#include <QThread>Worker::Worker(QObject *parent) : QObject{parent} {connect(this, &Worker::msg1, this, &Worker::msg1Slot);
}void Worker::workSlot() {qDebug() << "QThread begin";qDebug() << "child thread id" << QThread::currentThreadId();qDebug() << "QThread end";// 线槽函数已经执行完进入线程exec()中,可以通过发射信号重新让槽函数在线程中执行。也可以通过// quit() 退出线程exec()。
}void Worker::msg1Slot(const QString &m1) {qDebug() << QThread::currentThreadId() << "receive msg1 " << m1;
}void Worker::msg3Slot(const QString &m3) {qDebug() << QThread::currentThreadId() << "receive msg3 " << m3;
}

2)创建worker对象

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow), thread(nullptr) {ui->setupUi(this);worker = new Worker;qDebug() << "主线程" << QThread::currentThreadId();connect(worker, &Worker::msg2, this, &MainWindow::msg2Slot);connect(this, &MainWindow::msg3, worker, &Worker::msg3Slot);
}

直接在窗口构造函数中创建worker对象,程序运行期间这个对象不会被析构。

由于要使用moveToThread函数,所以worker不能设置父对象的,必须为空。

绑定了两个槽函数,分别用来响应消息2和消息3, 但是消息2的槽在窗口对象上,消息3的槽函数在worker对象上。

3)如果不创建子线程,看看3条消息的执行线程在哪?

 如果没有启用子线程的时候,3条消息执行函数都在主线程上。

4)启用子线程之后,3条消息的执行线程是什么?

void MainWindow::on_pushButtonStart_clicked() {qDebug() << "on_pushButtonStart_clicked线程" << QThread::currentThreadId();if (thread)return;thread = new QThread;worker->moveToThread(thread);QObject::connect(thread, &QThread::started, worker, &Worker::workSlot);QObject::connect(thread, &QThread::finished, this,&MainWindow::workerDetach);thread->start();
}

 这里创建了子线程,然后将worker对象移动线程内执行。 

workSlot()执行完毕之后其实线程并没有结束,它会执行exec()进入线程的消息循环

void Worker::workSlot() {qDebug() << "QThread begin";qDebug() << "child thread id" << QThread::currentThreadId();qDebug() << "QThread end";// 线槽函数已经执行完进入线程exec()中,可以通过发射信号重新让槽函数在线程中执行。也可以通过// quit() 退出线程exec()。
}

后面在worker上的信号或者槽绑定的触发都会进行响应。

 使用QThread启动子线程之后,消息1和消息3 的槽函数在子线程上执行了。

但是消息2的槽函数仍在主线程上。

重新复习一下信号槽绑定

Worker::Worker(QObject *parent) : QObject{parent} {connect(this, &Worker::msg1, this, &Worker::msg1Slot);
}

  connect(worker, &Worker::msg2, this, &MainWindow::msg2Slot);connect(this, &MainWindow::msg3, worker, &Worker::msg3Slot);

可以看出槽函数的执行线程就是对象所在的线程。

在创建了子线程之后,worker的执行线程已经发生了变化,所以消息1、3执行线程也变成了子线程。

5)停止子线程之后,恢复原来线程

QObject::connect(thread, &QThread::finished, this,&MainWindow::workerDetach);void MainWindow::workerDetach() {qDebug() << "workerDetach线程" << QThread::currentThreadId();worker->moveToThread(QThread::currentThread());
}

设置在线程退出之后重新移动对象到主线程。

这篇关于QThread 与QObject::moveToThread在UI中的应用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1083155

相关文章

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2

Python Dash框架在数据可视化仪表板中的应用与实践记录

《PythonDash框架在数据可视化仪表板中的应用与实践记录》Python的PlotlyDash库提供了一种简便且强大的方式来构建和展示互动式数据仪表板,本篇文章将深入探讨如何使用Dash设计一... 目录python Dash框架在数据可视化仪表板中的应用与实践1. 什么是Plotly Dash?1.1

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

Java中&和&&以及|和||的区别、应用场景和代码示例

《Java中&和&&以及|和||的区别、应用场景和代码示例》:本文主要介绍Java中的逻辑运算符&、&&、|和||的区别,包括它们在布尔和整数类型上的应用,文中通过代码介绍的非常详细,需要的朋友可... 目录前言1. & 和 &&代码示例2. | 和 ||代码示例3. 为什么要使用 & 和 | 而不是总是使

Python循环缓冲区的应用详解

《Python循环缓冲区的应用详解》循环缓冲区是一个线性缓冲区,逻辑上被视为一个循环的结构,本文主要为大家介绍了Python中循环缓冲区的相关应用,有兴趣的小伙伴可以了解一下... 目录什么是循环缓冲区循环缓冲区的结构python中的循环缓冲区实现运行循环缓冲区循环缓冲区的优势应用案例Python中的实现库

SpringBoot整合MybatisPlus的基本应用指南

《SpringBoot整合MybatisPlus的基本应用指南》MyBatis-Plus,简称MP,是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,下面小编就来和大家介绍一下... 目录一、MyBATisPlus简介二、SpringBoot整合MybatisPlus1、创建数据库和

python中time模块的常用方法及应用详解

《python中time模块的常用方法及应用详解》在Python开发中,时间处理是绕不开的刚需场景,从性能计时到定时任务,从日志记录到数据同步,时间模块始终是开发者最得力的工具之一,本文将通过真实案例... 目录一、时间基石:time.time()典型场景:程序性能分析进阶技巧:结合上下文管理器实现自动计时

Java逻辑运算符之&&、|| 与&、 |的区别及应用

《Java逻辑运算符之&&、||与&、|的区别及应用》:本文主要介绍Java逻辑运算符之&&、||与&、|的区别及应用的相关资料,分别是&&、||与&、|,并探讨了它们在不同应用场景中... 目录前言一、基本概念与运算符介绍二、短路与与非短路与:&& 与 & 的区别1. &&:短路与(AND)2. &:非短

Spring AI集成DeepSeek三步搞定Java智能应用的详细过程

《SpringAI集成DeepSeek三步搞定Java智能应用的详细过程》本文介绍了如何使用SpringAI集成DeepSeek,一个国内顶尖的多模态大模型,SpringAI提供了一套统一的接口,简... 目录DeepSeek 介绍Spring AI 是什么?Spring AI 的主要功能包括1、环境准备2

Spring AI与DeepSeek实战一之快速打造智能对话应用

《SpringAI与DeepSeek实战一之快速打造智能对话应用》本文详细介绍了如何通过SpringAI框架集成DeepSeek大模型,实现普通对话和流式对话功能,步骤包括申请API-KEY、项目搭... 目录一、概述二、申请DeepSeek的API-KEY三、项目搭建3.1. 开发环境要求3.2. mav