QThread 与QObject::moveToThread利用Qt事件循环在子线程执行多个函数

1. QThread的两种用法

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

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

2. moveToThread用法讲解

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

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

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

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

worker.h

cpp 复制代码
#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

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对象

cpp 复制代码
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条消息的执行线程是什么?

cpp 复制代码
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()进入线程的消息循环。

cpp 复制代码
void Worker::workSlot() {
    qDebug() << "QThread begin";
    qDebug() << "child thread id" << QThread::currentThreadId();
    qDebug() << "QThread end";

    // 线槽函数已经执行完进入线程exec()中,可以通过发射信号重新让槽函数在线程中执行。也可以通过
    // quit() 退出线程exec()。
}

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

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

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

重新复习一下信号槽绑定

cpp 复制代码
Worker::Worker(QObject *parent) : QObject{parent} {
    connect(this, &Worker::msg1, this, &Worker::msg1Slot);
}
cpp 复制代码
  connect(worker, &Worker::msg2, this, &MainWindow::msg2Slot);
    connect(this, &MainWindow::msg3, worker, &Worker::msg3Slot);

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

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

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

cpp 复制代码
QObject::connect(thread, &QThread::finished, this,
                     &MainWindow::workerDetach);

void MainWindow::workerDetach() {
    qDebug() << "workerDetach线程" << QThread::currentThreadId();
    worker->moveToThread(QThread::currentThread());
}

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

相关推荐
CodeCraft Studio4 小时前
数据透视表控件DHTMLX Pivot v2.1发布,新增HTML 模板、增强样式等多个功能
前端·javascript·ui·甘特图
伐尘5 小时前
【Qt】编译 Qt 5.15.x For Windows 基础教程 Visual Studio 2019 MSVC142 x64
windows·qt·visual studio
吃面不喝汤665 小时前
破解 Qt QProcess 在 Release 模式下的“卡死”之谜
开发语言·qt
charlie11451419112 小时前
逐步理解Qt信号与槽机制
数据库·qt
Anesthesia丶14 小时前
Vue3 + naive-ui + fastapi使用心得
vue.js·ui·fastapi
编程乐趣15 小时前
点下4个Winform UI开源控件库
ui·开源·mfc
yaso_zhang17 小时前
当生产了~/qt-arm/bin/qmake,可以单独编译其他-源码的某个模块,如下,编译/qtmultimedia
qt
code bean18 小时前
【Qt/C++】深入理解 Lambda 表达式与 `mutable` 关键字的使用
开发语言·c++·qt
老马啸西风1 天前
sensitive-word-admin v2.0.0 全新 ui 版本发布!vue+前后端分离
vue.js·ui·ai·nlp·github·word
爱看书的小沐1 天前
【小沐学GIS】基于C++绘制二维瓦片地图2D Map(QT、OpenGL、GIS)
c++·qt·gis·opengl·glfw·glut·二维地图