在QT中创建一个线程的方法有很多,与std::thread最为接近的是QtConcurrent。QT中有四个创建线程的方法分别是:
-
继承QThread对象
-
moveToThread方法
-
QRunnable方法
-
QtConcurrent::run方法
下面是对各种方法的优缺点总结:
方法 | 用法 | 注意事项 | 优点 | 缺点 |
---|---|---|---|---|
继承QThread | 首先,创建一个继承自 QThread 的类并对其 run 方法进行重写。接着,实例化这个类并用 start 方法启动线程。 | 当run() 方法执行完毕,线程会自动结束。不建议在派生的QThread 类中使用槽。 | 提供了完整的线程管理功能和与其他线程的通信功能 | 需要手动继承和重写方法,处理线程的生命周期可能有挑战,例如正确地回收线程资源 |
QObject::moveToThread | 首先实例化一个QObject 对象和一个QThread 对象。然后QObject 的moveToThread() 方法来将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的例子