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());
}

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

相关推荐
----云烟----5 小时前
QT中QString类的各种使用
开发语言·qt
芋芋qwq7 小时前
Unity UI射线检测 道具拖拽
ui·unity·游戏引擎
鸿蒙自习室8 小时前
鸿蒙多线程开发——线程间数据通信对象02
ui·harmonyos·鸿蒙
「QT(C++)开发工程师」10 小时前
【qt版本概述】
开发语言·qt
大霞上仙11 小时前
element ui table 每行不同状态
vue.js·ui·elementui
一路冰雨14 小时前
Qt打开文件对话框选择文件之后弹出两次
开发语言·qt
老赵的博客15 小时前
QT 自定义界面布局要诀
开发语言·qt
码码哈哈0.015 小时前
VSCode 2022 离线安装插件QT VSTOOl报错此扩展不能安装在任何当前安装的产品上。
ide·vscode·qt
栈老师不回家18 小时前
Element UI 组件库详解【Vue】
前端·vue.js·ui
feiyangqingyun19 小时前
Qt/C++离线地图的加载和交互/可以离线使用/百度和天地图离线/支持手机上运行
c++·qt·qt天地图·qt离线地图·qt地图导航