【Qt】Qt多线程开发—QThread

一、QThread的两种使用方法

QThread是Qt中用于在多线程中运行代码的核心类,该类是QObject的一个子类。

​ 关于QThread如何使用,Qt官方提供了两种方法:

(1-1)方法一

​ 步骤一:子类化QThread并重新实现run()。

​ 步骤二:然后创建子类的实例并调用start()成员函数运行线程。

如下代码片段:

class MyThread : public QThread {
private:
	void run() override {
	// 要在新线程中运行的代码
	}
};

MyThread *thread = new MyThread;
thread->start(); // 调用run()启动一个新线程
// ...
thread->wait(); // 等待线程完成

线程具有优先级,在调用start()的时候可以指定优先级参数,或者使用setPriority()更改线程的优先级。
​当从run()返回时(一段时间后)线程将停止运行,具体时间长短与操作系统相关。

QThread::isRunning()QThread::isFinished()提供关于线程执行的信息,在开发中,可以通过这两个成员函数获取到线程的运行状态。

QThread提供了QThread::started()(当线程开始时发射)和QThread::finished()(在线程运行结束后发射)两个信号。

​一个线程可以通过调用QThread:sleep()函数临时停止执行。通常这是一种不好的使用方法,但是比事件驱动(或轮询)要好很多。

​通过QThread调用wait()来等待线程执行完成,可选择性向该函数传递等待的最大毫秒数。

(1-2)方法二

​ 第二种使用QThread创建、管理线程的方法:本质上不是派生QThread,而是对象关联。步骤如下:
(1)创建QThread
(2)使用moveToThread()将对象添加到QThread管理的线程中运行。
如下代码片段:

auto thread = new QThread;

auto worker = new Worker;

connect(thread, &QThread::started, worker, &Worker::doWork);
connect(worker, &Worker::workDone, thread, &QThread::quit);

connect(thread, &QThread::finished, worker, &Worker::deleteLater);

worker->moveToThread(thread);
thread->start();

​ QThread的第二种使用方法,允许使用者在多线程中运行代码而不需要子类化QThread。

二、Qt线程运行的两种策略

​ 在线程中运行代码有两种基本策略:不带事件循环带事件循环

(1)不带事件循环

​ 派生类QThread并重载QThread::run()成员函数。创建一个实例并使用QThread::start()启动新线程。

class MyThread : public QThread {
private:
	void run() override {
		loadFilesFromDisk();
		doCalculations();
		saveResults();
	}
};
auto thread = new MyThread;
thread->start();

thread->wait();

(2)带事件循环

​ 当在处理定时器、网络、队列信号/槽连接等问题时,这时事件循环是必须的。

​ Qt支持在单个线程中事件循环,如下图所示:
在这里插入图片描述

​ 每个线程的本地事件循环都为存在于该线程中的QObjects交付事件,如果没有事件循环,存在于对应线程中的对象将接收不到事件。

​ 通过在run()中调用QThread::exec()来启动线程的本地事件循环。如下代码片段:

class MyThread : public QThread {
private:
	void run() override {
    auto socket = new QTcpSocket;
    socket->connectToHost(...);

    exec(); // 运行事件循环

	// 执行一些清除操作
    }  
};

QThread::quit()QThread::exit()将退出事件循环。可以使用QEventLoop或者手动调用QCoreApplication::processEvents()处理事件。

​ 注:QThread::run()的默认实现实际上调用了QThread: exec ()。如果重载了QThread::run(),使用者就必须手动调用QThread: exec ()

三、QThread的使用注意事项

​ 在非主线程中不能执行以下四种类型的操作:

(1)不能执行对GUI 控件的操作。包括但不限于:使用任何QWidget / Qt Quick / QPixmap API。这些情况是除外:使用QImage, QPainter等;使用OpenGL可能是可以的:但是需要在运行时调用QOpenGLContext: supportsThreadedOpenGL ()事先进行检查。

(2)不能调用Application::exec()。

(3)在销毁相应QThread对象之前,一定要销毁次要线程中的所有QObject。【这一点非常重要】。

(4)不要阻塞GUI线程。

​ 如何确保QObjects被销毁?如下:

​ (1)在QThread::run()函数堆栈上创建QObject,即在函数内部创建对象。

​ (2)将对象的QObject::deleteLater()槽函数连接到QThread::finished()信号。这样,当线程执行完成后,程序将找机会删除创建的对象。

​ (3)把QObject从线程里移出来。

​ 例如以下代码片段:

class MyThread : public QThread {
private:
	void run() override {
		MyQObject obj1, obj2, obj3;

		QScopedPointer<OtherQObject> p;
		if (condition)
			p.reset(new OtherQObject);

		auto anotherObj = new AnotherQObject;
		connect(this, &QThread::finished,anotherObj, &QObject::deleteLater);

		auto yetAnother = new YetAnotherQObject;

        // 在退出线程之前,将该对象移动到主线程中  
        yetAnother->moveToThread(qApp->thread());
        // 从这一行代码后,就不能使用或操作yetAnother了
	}
};
四、总结

Qt下多线程开发,使用QThread比较方便。本文记录了使用QThread的两种方法,以及QThread中线程运行的事件交付方式,也记录关于QThread的一些注意事项。


搜索/关注【嵌入式小生】vx公众号,获取更多精彩内容>>>>
请添加图片描述

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

iriczhao

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

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

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

打赏作者

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

抵扣说明:

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

余额充值