最详细的Qt多线程的三种方法之一QThread

本文详细介绍了在Qt中使用QThread创建和管理多线程的方法,包括继承QThread和使用QObject::moveToThread。重点讲解了如何通过信号-槽机制实现线程间的通信,并列举了不宜使用的实践方式和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、多线程目的

QThread类提供了一个与平台无关的管理线程的方法。
在Qt中建立线程的主要目的就是为了用线程来处理那些耗时的后台操作,比如大量运算,复制大文件,网络传输等。

二、QThread多线程使用方法

  • 使用Qt框架开发应用程序时,使用QThread类可以方便快捷地创建管理多线程。
  • 而多线程之间的通信也可使用Qt特有的“信号-槽”机制实现。
    QThread的使用方法有如下两种:
    1. 继承QThread类
    2. QObject::moveToThread()

2.1 继承QThread方法

第一种方法很简单,也很好理解,写一个类继承QThread类,并重写run()函数,并在主线程中生成一个ChildThread的实例,并调用对象的start()函数

首先定义FileCopyThread类,继承QThread,添加两个signals
//FileCopyThread.h

#ifndef CHILDTHREAD_H
#define CHILDTHREAD_H

#include <QThread>

class FileCopyThread : public QThread
{
    Q_OBJECT
public:
    explicit FileCopyThread();
    ~FileCopyThread();

protected:
    void run() override;   //重写QThread类的虚函数,也是线程子类的入口函数

signals:
    void done();                        //完成信号
    void reportProgress(int precent);   //报告完成进度
};

#endif // CHILDTHREAD_H

FileCopyThread.cpp

#include "childthread.h"
#include <QMessageBox>

FileCopyThread::FileCopyThread()
{

}

FileCopyThread::~FileCopyThread()
{

}

void FileCopyThread::run()
{
    for (int i = 0; i <= 10; i++)//模拟耗时运算
    {
        QThread::msleep(500);
        emit reportProgress(i*10);
    }
    emit done();
}

在主线程中调用如下:

 FileCopyThread* t = new FileCopyThread;
    connect(t, &FileCopyThread::reportProgress, ui->progressBar, &QProgressBar::setValue);
    connect(t, SIGNAL(done()), this, SLOT(onCopyFinished()));
    t->start();

请添加图片描述

2.2 QObject::moveToThread

说实话这种方法我没有很理解o(╥﹏╥)o

  1. 定义一个普通的QObject派生类FileWorker,然后将其对象move到QThread中。
  2. 在定义一个转发类也是QObject子类,起名字叫controller,或者叫dummy。将转发类的信号槽和FileWorker类的信号槽关联起来,这样在主线程中调用转发类的槽函数,或者接收信号就OK了。
    大概意思是通过转发类,能使得FileWorker类的槽函数妥当的运行在子线程里面。同时也不需要使用QMutex来进行同步,Qt的事件循环会自己自动处理好这个。
    //FileWorker.h
#ifndef FILEWORKER_H
#define FILEWORKER_H

#include <QObject>
#include <QThread>

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

signals:
    void done();                        //完成信号
    void reportProgress(int precent);   //报告完成进度

private slots:
    void doWork();//线程需要做的工作
};

#endif // FILEWORKER_H

//FileWorker.cpp

#include "fileworker.h"
#include <QDebug>

FileWorker::FileWorker(QObject *parent) : QObject(parent)
{

}

void FileWorker::doWork()
{
    qDebug()<<"from thread doWork slot:" <<QThread::currentThreadId();
    for (int i = 0; i <= 10; i++)//模拟耗时运算
    {
        QThread::msleep(500);
        emit reportProgress(i*10);
    }
    emit done();
}

//Controller.h

#ifndef CONTROLLER_H
#define CONTROLLER_H

#include <QObject>

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

signals:
    void sig_copy();

    void done();                        //完成信号
    void reportProgress(int precent);   //报告完成进度

public slots:
    void startCopy();
};

#endif // CONTROLLER_H

//Controller.cpp

#include "controller.h"

Controller::Controller(QObject *parent) : QObject(parent)
{

}

void Controller::startCopy()
{
    emit sig_copy();
}

在主线程中,首先做转发类和worker类的信号槽关联

    worker2.moveToThread(&t2);//FileWorker对象moveToThread
    connect(&controller2, SIGNAL(sig_copy()), &worker2, SLOT(doWork()));//controller2是这里的转发类,它的信号和fileWorker槽关联起来
    connect(&worker2, SIGNAL(reportProgress(int)), ui->progressBar, SLOT(setValue(int)));
    connect(&worker2, SIGNAL(done()), this, SLOT(onCopyFinished()));

最后启动线程即可,运行效果和方法一一致。

//方法二
void Dialog::on_pushButton_2_clicked()
{
    qDebug()<<"UI thread:"<<QThread::currentThreadId();
    t2.start();
    controller2.sig_copy();
}

三、QThread总结

  • 推荐做的:
    在QThread子类添加信号。这是绝对安全的,并且也是正确的(发送者的线程依附性没有关系)

  • 不应该做的是:
    调用moveToThread(this)函数
    指定连接类型:这通常意味着你正在做错误的事情,比如将QThread控制接口与业务逻辑混杂在了一起(而这应该放在该线程的一个独立对象中)
    在QThread子类添加槽函数:这意味着它们将在错误的线程被调用,也就是QThread对象所在线程,而不是QThread对象管理的线程。这又需要你指定连接类型或者调用moveToThread(this)函数
    使用QThread::terminate()函数

  • 不能做的是:
    在线程还在运行时退出程序。使用QThread::wait()函数等待线程结束
    在QThread对象所管理的线程仍在运行时就销毁该对象。如果你需要某种“自行销毁”的操作,你可以把finished()信号同deleteLater()槽连接起来

    好了,路漫漫其修远兮,吾将上下而求索。
    关于多线程QRunnable的用法,可以参考我的另一篇博客Qt多线程的三种方法之二QtRunnable::run()+QThreadPool
    关于多线程QConcurrent的用法,可以参考我的另一篇博客Qt多线程的三种方法之三QtConcurrent::run()+QThreadPool

参考:https://blog.csdn.net/zb872676223/article/details/22718087

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zy100Papa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值