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

一、多线程目的

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

  • 5
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
QThreadQt 框架中提供的用于多线程编程的类。当 QThread 失效时,可能是由于以下原因之一: 1. 错误的使用方式:QThread 的正确使用方式是将需要在新线程中执行的任务放在一个自定义的 QObject 子类中,并将其移动到 QThread 实例中。然后,通过调用 QObject::moveToThread() 方法将该对象移动到新线程中。如果没有正确地将任务对象移动到新线程,那么新线程将不会执行任何操作。 2. 事件循环问题:QThread 在新线程中创建一个事件循环,以处理该线程中的事件。如果没有调用 QThread::exec() 方法启动事件循环,那么新线程将不会执行任何操作。确保在 QThread 对象创建后调用 exec() 方法。 3. 对象生命周期问题:在使用 QThread 时,需要注意对象的生命周期。当 QThread 对象被删除时,它会尝试终止线程并删除所有属于该线程的对象。如果任务对象的生命周期与 QThread 对象不一致,可能会导致线程失效。 4. 并发控制问题:当多个线程同时访问共享资源时,需要进行适当的并发控制,以防止竞争条件和数据不一致性。Qt 提供了一些并发编程工具,如互斥锁(QMutex)和信号量(QSemaphore),可以用于线程间的同步和共享数据访问。 这些是导致 QThread 失效的一些常见原因。如果遇到问题,建议仔细检查代码,确保正确使用 QThread,并排除其他可能的问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

苏克贝塔

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

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

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

打赏作者

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

抵扣说明:

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

余额充值