Qt中另一种创建线程的方法
上一篇博客中,我介绍了一种Qt中经典的的创建线程的方法:继承QThread类、子类中重写run()函数的方法。经典方法虽然使用简单,但是有一个问题:在合理的软件架构中,代码中出现的继承关系,也应该真实反应需求中继承关系,思考一下:通过继承的方式实现新的线程类,有什么实际的意义?我觉得完全没有实际的意义,仅仅是为了重写run()函数。但是虽然没有实际意义,但是也不能说它是错误的,只不过在逻辑上有点背离面向对象。
下面开始介绍新的创建线程的方法
首先,我们得知道Qt4.4版本之前,QThread::run()是纯虚函数:virtual void run() = 0;
QThread::run()仅仅是个声明,没有任何属性,子类是必须完全重写run()的,这个时候我们也只有这一种创建多线程的方法。但是在Qt4.4版本之后,Qt对QThread::run()做了修改:
virtual void run()
{
exec();
}
run()函数就默认调用了exec()事件循环,使得这第二种创建线程的方法得以实现:使用信号与槽机制,将一个槽函数作为线程的入口函数。方法是这个方法,具体实现的步骤,按照我的经验总结一些:
- 在当前类中定义一个private QThread类对象
- 在当前类中定义一个private 的槽函数,这个槽是作为线程入口函数,所有需要在新线程中实现的操作,都在这里编写。
- 当然就是链接信号与槽了。线程的start()的信号链接到这个槽函数。
- 将当前对象moveToThread到定义的线程对象。
下面是示例代码:
//以下是main.cpp
#include <QCoreApplication>
#include "QDebug"
#include "testcl.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"main threadID :"<<QThread::currentThreadId()<<endl;//打印主线程ID
//
testCl cl;
cl.threadStart();
cl.threadTerminate();
return a.exec();
}
//以上是main.cpp
//以下是testcl.h
#ifndef TESTCL_H
#define TESTCL_H
#include <QObject>
#include "QThread"
#include "QDebug"
class testCl : public QObject
{
Q_OBJECT
public:
explicit testCl(QObject *parent = nullptr);
void threadStart();
void threadTerminate();
private:
QThread newThread;//定义一个QThread对象,事实上,QThread类中实现了
//系统层面的线程函数
private slots:
void slotThread();
signals:
};
#endif // TESTCL_H
//以上是testcl.h
//以下是testcl.cpp
#include "testcl.h"
testCl::testCl(QObject *parent) : QObject(parent)
{
connect( &newThread,SIGNAL(started()),this,SLOT(slotThread()));
moveToThread(&newThread);
}
void testCl::threadStart()
{
newThread.start();
qDebug()<<"threadStart ID :"<<thread()->currentThreadId()<<endl;//打印这个对象的普通函数的线程ID
}
void testCl::threadTerminate()
{
newThread.terminate();
qDebug()<<"threadTerminate ID :"<<thread()->currentThreadId()<<endl;//打印这个对象的普通函数的线程ID
}
void testCl::slotThread()
{
qDebug()<<"slotThread ID :"<<thread()->currentThreadId()<<endl;//打印这个对象的线程槽函数的ID
}
//以上是testcl.cpp
以上代码是使用新的方法创建了线程,分别要打印主线程ID,两个类中普通成员函数所在的线程ID、连接到新线程的槽函数的线程ID,按照我们的理解,我们在main()函数中定义了testcl对象,那么testcl所有普通成员变量和成员函数都应该与main()函数线程保持一致,而槽函数是作为新线程入口函数的,线程ID号肯定与main()函数不一样。
运行一下,看看结果是否如我们所想:
运行结果如我们所想。这也证明了新的线程创建方法是有效的,综合整个过程,我们能够得到如下结论:
- Qt中经典的线程创建方法,是基于继承QThread再重写入口函数的方法,属于类与类继承的方式;但是今天介绍的方法,是属于类与类组合的方式,在线程创建这一点上,显然是组合的方式更符合逻辑。
- 这个创建线程方式能够实现,得益于QThread::run()中默认调用exec();线程启动后,先是执行run()函数中exec(),进入了事件循环,信号与槽机制生效,这个时候发出的started()信号才能进入槽。
- Qt官方推荐使用今天的新方式进行线程创建。
原创内容,禁止转载
王雄 第一次更新于2021.03.07