QT 中的多线程之 moveToThread

1. 概述

在 Qt 中,多线程也被广泛用于实现后台任务、异步操作、多任务处理等功能。通过使用多线程,可以提高程序的响应性和用户体验,同时也可以提高程序的运行效率和稳定性。

其中 QThread 是多线程中使用最多的,它提供了两种方法来创建和控制线程的类:

  • 继承 QThread 子类,并在子类中实现 run() 方法
  • 使用 QObject::moveToThread() 方法

本文重点介绍 moveToThread() 方法,它是将 QObject 的子对象移动到另一个线程的机制,这样对象可以在单独的线程中处理信号和槽函数,从而释放主线程用于执行其他任务。

2. 方法描述

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


// 创建 Worker 对象
Worker* worker = new Worker;

// 创建一个新的线程
QThread* _workerThread = new QThread;

// 将Worker对象移动到新线程
worker ->moveToThread(_workerThread );

// 连接Worker对象的信号和槽
// 该线程结束时销毁
connect(_workerThread, &QThread::finished, worker, &QObject::deleteLater);
// 线程结束后发送信号,对结果进行处理
connect(worker, SIGNAL(resultReady(int)), this, SLOT(handleResults(int)));

// 启动新线程
_workerThread ->start();


如果这个 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)
    : QObject(parent)
{
}

void Worker::doWork(int parameter) {
    qDebug() << __FUNCTION__ << " Thread ID: " << QThread::currentThreadId() << "\n";
    emit resultReady(parameter);
}

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

#ifndef WORKERCONTROLLER_H
#define WORKERCONTROLLER_H

//
// WorkerController .h
//
#include <QThread>
#include <QDebug>
#include "worker.h"

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

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

    ~WorkerController() override ;

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

signals:
    void operate(const int);  // 发送信号,触发线程

private:
    QThread*      _workerThread = nullptr;
};

#endif // WORKERCONTROLLER_H

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

/* 在 构造函数中创建 worker 对象,并且将其事件循环全部交给_workerThread 对象来处理,最后启动该线程,然后触发其事件处理函数。*/
WorkerController::WorkerController(QObject *parent) : QObject(parent) {
    //moveToThread 通用流程:
    auto *worker = new Worker ;
    _workerThread= new QThread(this);
    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() << __FUNCTION__ << " Thread ID:" << QThread::currentThreadId() << '\n' ;
    emit operate(9);
}

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

void WorkerController::handleResults(const int result) {
    qDebug() << __FUNCTION__ << " Thread ID:" << QThread::currentThreadId() << ", last result: " << result;
}

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

#include <QCoreApplication>
#include <QThread>
#include "workercontroller.h"

int main(int argc, char *argv[])
{
    qDebug() << __FUNCTION__ << " Thread ID:" << QThread::currentThreadId() << '\n' ;
    QCoreApplication a(argc, argv);
    WorkerController c ;
    return a.exec();
}

4. 运行结果

在这里插入图片描述
doWork 槽函数中为新的线程

5. 注意事项

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

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

6. 结语

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

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

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

  • 10
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt多线程可以使用`QThread`类来实现,而`moveToThread`是一个很常用的函数,用于将一个QObject对象移动到另一个线程执行。 下面是一个使用`moveToThread`的简单例子: ```cpp #include <QCoreApplication> #include <QThread> #include <QDebug> class Worker : public QObject { Q_OBJECT public: Worker(QObject *parent = nullptr) : QObject(parent) {} public slots: void doWork() { qDebug() << "Worker thread:" << QThread::currentThreadId(); } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Worker worker; QThread thread; worker.moveToThread(&thread); QObject::connect(&thread, &QThread::started, &worker, &Worker::doWork); QObject::connect(&worker, &Worker::destroyed, &thread, &QThread::quit); thread.start(); return a.exec(); } ``` 在上面的例子,我们创建了一个`Worker`类,它继承自`QObject`类,并有一个`doWork`槽函数。我们将`worker`对象通过`moveToThread`函数移动到了`thread`线程。然后,我们通过`connect`函数将`thread`的`started`信号连接到`worker`的`doWork`槽函数上,当线程启动时,`doWork`槽函数会在`thread`线程执行。同时,我们还将`worker`的`destroyed`信号连接到`thread`的`quit`槽函数上,以保证线程能够正确退出。 需要注意的是,如果我们将一个QObject对象移动到了另一个线程执行,那么它的所有信号和槽函数都必须在该线程执行,否则会出现问题。所以,在上面的例子,我们将`worker`对象的`doWork`槽函数定义为`public slots`,并且在`thread`线程执行,以保证它能正确执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值