QT多线程简单使用

13 篇文章 2 订阅

在QT中创建一个线程的方法有很多,与std::thread最为接近的是QtConcurrent。QT中有四个创建线程的方法分别是:

  • 继承QThread对象

  • moveToThread方法

  • QRunnable方法

  • QtConcurrent::run方法

下面是对各种方法的优缺点总结:

方法用法注意事项优点缺点
继承QThread首先,创建一个继承自 QThread 的类并对其 run 方法进行重写。接着,实例化这个类并用 start 方法启动线程。run()方法执行完毕,线程会自动结束。不建议在派生的QThread类中使用槽。提供了完整的线程管理功能和与其他线程的通信功能需要手动继承和重写方法,处理线程的生命周期可能有挑战,例如正确地回收线程资源
QObject::moveToThread首先实例化一个QObject对象和一个QThread对象。然后QObjectmoveToThread()方法来将QObject(及其任何子对象)从它当前的线程上下文移动到与QThread实例关联的线程1. 确保移动的时候不要被其他线程使用 2.QObject及其所有子对象必须始终位于同一个线程提供了线程间通信的方便,移动对象到线程比继承QThread更灵活、更简单虽然比继承QThread简单,但仍需要注意线程生命周期的管理,确保正确地回收资源
继承QRunnable创建一个继承QRunnable的类并重写run方法,然后通过QThreadPool启动默认情况下,QRunnable对象的autoDelete属性被设置为true,这意味着一旦任务完成,它会被自动删除。可以轻松与线程池结合使用,自动管理线程资源,可以多次启动相同的任务QThread相比,为了进行通信,你可能需要使用其他手段如信号和槽或其他机制
QtConcurrent::run调用QtConcurrent::run函数,传入一个函数、lambda或其他可调用对象,以并发方式执行如果线程池满,提交的任务可能会延迟执行,直到线程池中有可用线程使用极其简单,能与QFuture结合以获得异步结果,无需手动管理线程生命周期对于复杂的线程间通信和同步可能不够灵活,线程池满时可能无法立即执行任务

为了方便使用Qt的信号与槽机制,我们常常采用表格的前两种方法,即继承QThread或者使用moveToThreafd方法;如果你没有用到信号与槽,那么你完全可以采用std::thread或者QtConcurrent::run方法。
为了支持信号与槽的机制,我们通常会使用前两种方法,也就是重写run或者moveToThread。moveToThread的优点在于你可以让这个继承QThread类的所有槽都运行在新线程内,而

本文讲述的是使用QThread进行管理线程创建方法,也就是上述表格的前两个。在使用之前首先需要对QThread的类有一定的理解:

一、QThread管理

QThread类是QT提供的一个与平台无关的管理线程的类。一个QThread对象管理一个程序中的线程,run函数是实际执行的内容。默认情况下,run将会通过的调用exec()来启动事件循环并在线程内启动事件循环。

在介绍这个方法之前有必要对QThread对象做个简单的介绍。以下内容属于搬运[1]:

1.1 槽函数
 void quit();//Equivalent to calling QThread::exit(0)
 void start(QThread::Priority priority = InheritPriority);
 void terminate();

quit() 停止线程的事件循环。如果线程没有事件循环等于什么都不做。

start() 开启线程。开启线程将会调用run()方法,操作系统将会根据优先级安排线程的执行,如果一个线程已经被运行,那么槽函数将不会做任何事情。

terminate() 终止一个线程。线程不一定会立刻终止,这取决于操作系统的调度策略,在调用terminate()后使用QThread::wait() ,wait()函数和POSIX的 pthread_join() 功能是一样的。不推荐使用,因为线程的资源可能因为你的强制终止而永远得不到释放。

1.2 信号

void finished();
void started();

finished() 如果相关线程完成了执行将会发出此信号。这意味着事件循环将只剩下延迟删除的事件。注意,如果你调用terminate()是否会发出这个信号是不一定的,他是一个内部信号。

started() 相关线程在run()函数被调用之前将发出此信号。

1.3 受保护的函数

int exec();
virtual void run();

exec()调用将使得线程进入事件循环并等到exit()被调用,返回值是传递给exit的数值,如果返回值是0,说明是调用quit退出的。我们一般不需要手动执行exec()的原因是因为run()默认实现会调用exec。

run()是线程的入口,当线程对象调用start()新创建的线程将会调用该函数。默认情况run()函数实现将会只调用exe函数,重新实现run函数以便定制高级的线程管理,该函数返回线程将会停止执行。

1.4 其他重要公共函数

void exit(int returnCode=0);
bool wait(unsigned long time = ULONG_MAX);
void setPriority(QThread::Priority priority)

exit()停止事件循环,在QEventLoop::exec()执行后返回,返回值将是退出码。0表示正常退出,非零表示有错误。事件循环将会停止,直至再次调用QThread::exec()。

wait() 将会阻塞线程至满足下面两个条件的其中一个:

  • 线程执行完毕。run()返回或者线程尚未开始
  • 定时结束。默认是永远不会返回,也就是只能是顶一个,当然你设置了超时时间,那么将会返回一个false并结束阻塞。

下面是状态指示:

bool isFinished();
bool isRunning()const;

下面这个是用来优雅退出一个线程的方法:

bool isInterruptionRequested()const;
void requestInterruption();

具体用法参照这里

1.5 重要静态方法

void sleep(unsigned long secs);
void msleep(unsigned long msecs);
void usleep(unsigned long usecs);
int QThread::idealThreadCount()

返回 idealThreadCount()当前处理器的最佳线程数。

二、继承QThread方法

#include <QCoreApplication>
#include <QThread>
#include <QDebug>

class MyThread:public QThread{
    protected:
        void run() override;
};

void MyThread::run()
{
    int i=0;
    for(int j=0;j<10;j++)
    {
        qDebug()<<i++;
        QThread::sleep(1);
    }
    qDebug()<<"线程退出成功";
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    MyThread * pMythread=new MyThread;
    pMythread->start();
    QObject::connect(pMythread,&QThread::finished,[&a](){a.exit();});

    return a.exec();
}

运行结果如下:
在这里插入图片描述

三、QObject::moveToThread方法

#include <QObject>
#include <QDebug>
#include <QThread>

class Worker : public QObject {
    Q_OBJECT

public slots:
    void doWork() {
        int i=0;
        for(int j=0; j<10; j++) {
            qDebug() << i++;
            QThread::sleep(1);
        }
        qDebug() << "线程退出成功";
        emit workFinished();
    }

signals:
    void workFinished();
};
#include <QCoreApplication>
#include <QPushButton>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QThread thread;
    Worker worker;

    worker.moveToThread(&thread);

    // 使用新的连接语法
    QObject::connect(&thread, &QThread::started, &worker, &Worker::doWork);
    QObject::connect(&worker, &Worker::workFinished, &thread, &QThread::quit);
    QObject::connect(&worker, &Worker::workFinished, &worker, &Worker::deleteLater);
    QObject::connect(&thread, &QThread::finished, &thread, &QThread::deleteLater);

    thread.start();

    return a.exec();
}

[1] https://blog.csdn.net/luoyayun361/article/details/97150788

[20200729] 一个对象moveToThread后,对该对象所有操作都是在新的线程中执行的。如一个对象k移入新线程中,之后调用a b方法中打印QThread::currentThreadId()都是同一个。
[20211022] 更新了Qt几种创建线程的方法及其优缺点分析
[20230825] 修改了moveToThread的例子

  • 2
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值