【Qt】多线程QThread::run()与QObject::moveToThread()

本文介绍了Qt中两种实现多线程的方法:一是继承QThread并重写run()函数,二是使用QObject::moveToThread。QThread的run()函数执行完毕意味着子线程结束,可以发射信号通知主线程。moveToThread方法则允许将工作对象移动到指定线程并在其中执行槽函数,通过信号与槽进行通信,更符合Qt的事件驱动模型。
摘要由CSDN通过智能技术生成

官方链接QThread Class | Qt Core 5.15.14

使用QThread::run()

简单来说就是继承QThread类,并重写run()函数,这样run()函数中的代码就会运行在子线程中。

QThread对象管理着一个线程,并通过start函数启动这个线程,线程要执行的代码都在run()里面,run()函数的进入和返回,就相当于子线程的启动和结束。

但是run()函数何时返回呢?一般有三种情况:

  1. run中代码走一遍就返回了。
  2. run中有while循环,在循环中有退出循环的flag变量,由外部程序置位这个flag,使循环退出,从而返回。
  3. run中最后一行是事件循环exec(),即程序会卡在这一行:this->exec();这种情况需要调用exit()或者terminate()才能让事件循环停下,从而让run返回。

当run函数执行完毕的时候,新线程也就结束了,并且发出finished()信号。

#ifndef SOMETHINGTHREAD_H
#define SOMETHINGTHREAD_H

#include <QThread>
#include <QObject>

class SomethingThread : public QThread
{
    Q_OBJECT
public:
    explicit SomethingThread(QObject *parent = nullptr);

    void run() override;//线程函数

private: signals:
    void resultReady(const QString result);//如果有线程计算结果返回,可以通过这种方式

};

#endif // SOMETHINGTHREAD_H
#include "somethingthread.h"
#include "qdebug.h"

SomethingThread::SomethingThread(QObject *parent)
    : QThread{parent}
{

}

void SomethingThread::run()
{
     for(int i=0;i<10;i++)
     {
         qDebug()<<"doing something %d"<<i<<" thread_id"<<QThread::currentThreadId();
         QThread::sleep(1);
     }
     emit this->resultReady("finish");//触发信号,传递结果参数
}
    SomethingThread* sth=new SomethingThread(this);
    connect(sth,&SomethingThread::resultReady,this,&SIHToolBar::onResultReady);//使用接收结果
    connect(sth,&SomethingThread::finished,sth,&QObject::deleteLater);
    sth->start();


void SIHToolBar::onResultReady(const QString result)
{
    qDebug()<<"onResultReady result="<<result<<" thread_id="<<QThread::currentThreadId();
}
doing something %d 0  thread_id 0x6188
doing something %d 1  thread_id 0x6188
doing something %d 2  thread_id 0x6188
doing something %d 3  thread_id 0x6188
doing something %d 4  thread_id 0x6188
doing something %d 5  thread_id 0x6188
doing something %d 6  thread_id 0x6188
doing something %d 7  thread_id 0x6188
doing something %d 8  thread_id 0x6188
doing something %d 9  thread_id 0x6188
onResultReady result= "finish"  thread_id= 0x2f0

有几点需要注意:

  • QThread类中的run()函数默认实现是这样的QThread::run() { this->exec(); },也就是默认开启了事件循环。
  • 只有run函数中的代码段是在子线程中执行的。如果run中使用了成员变量,而且其他地方也使用到了它,这时需要自行检查是否线程安全。
  • run函数中发射的信号,连接了使用它的对象的槽函数,实际上是跨线程了的。
  • 官方不推荐使用这种方式

使用QObject::moveToThread()

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

#ifndef WORKER_H
#define WORKER_H

#include <QObject>

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

signals:
    void resultReady(const QString& result);//线程完成工作时发送的信号

public slots:
    void doWork(const QString& param);//定义了线程要执行的操作

};

#endif // WORKER_H
#include "worker.h"
#include <QDebug>
#include <QThread>

Worker::Worker(QObject *parent)
    : QObject{parent}
{

}


void Worker::doWork(const QString &param)
{
    for(int i=0;i<10;i++)
    {
        qDebug()<<"doWork %d"<<i<<" thread_id"<<QThread::currentThreadId();
        QThread::sleep(1);
    }
    emit this->resultReady("finish");//触发信号,传递结果参数
}
#ifndef CONTROLLER_H
#define CONTROLLER_H

#include <QObject>
#include <QThread>
class Controller : public QObject
{
    Q_OBJECT
    QThread workerThread;
public:
    explicit Controller(QObject *parent = nullptr);
    ~Controller();

signals:
    void doWork(const QString& param);//发送信号 触发线程

public slots:
    void onResultReady(const QString& result);//处理子线程执行的结果

};

#endif // CONTROLLER_H

#include "controller.h"
#include "worker.h"
#include <QDebug>
Controller::Controller(QObject *parent)
    : QObject{parent}
{
    Worker* work=new Worker();
    work->moveToThread(&this->workerThread);//调用moveToThread将该任务交给

    connect(&this->workerThread,&QThread::finished,work,&QObject::deleteLater);//当线程结束的时候,销毁worker对象
    connect(this,&Controller::doWork,work,&Worker::doWork);//通过controller的dowork信号 连接 worker对象的doworker槽函数
    connect(work,&Worker::resultReady,this,&Controller::onResultReady);
    workerThread.start();

    qDebug()<<"main thread_id="<<QThread::currentThreadId();
    emit this->doWork("start");
    //work->doWork("start");//这种直接调用的方式没有在子线程中执行
}

Controller::~Controller()
{
    workerThread.quit();
    workerThread.wait();
}

void Controller::onResultReady(const QString &result)
{
    qDebug()<<"onResultReady result="<<result<<" thread_id="<<QThread::currentThreadId();
}
main thread_id= 0x7254
doWork %d 0  thread_id 0x5970
doWork %d 1  thread_id 0x5970
doWork %d 2  thread_id 0x5970
doWork %d 3  thread_id 0x5970
doWork %d 4  thread_id 0x5970
doWork %d 5  thread_id 0x5970
doWork %d 6  thread_id 0x5970
doWork %d 7  thread_id 0x5970
doWork %d 8  thread_id 0x5970
doWork %d 9  thread_id 0x5970
onResultReady result= "finish"  thread_id= 0x7254

有几点需要注意:

  • 任何对象只要执行了moveToThread,那么该对象的所有槽函数就会在子线程执行(前提是,该槽函数是被信号触发的),如果直接显示调用这些槽函数,仍然会运行在原线程,不会出现多线程的效果。
  • 我们可以在一个worker类中定义多个需要做的工作(槽函数),然后触发信号,子线程就可以执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值