[参考资料]https://zhuanlan.zhihu.com/p/53270619
1.曾经的Qt多线程
在 Qt 4.4 版本以前的 QThread 类是个抽象类,要想编写多线程代码唯一的做法就是继承 QThread 类,并在 run() 函数中塞入耗时操作代码.在 Qt 4.4 版本后, Qt 官方对 QThread 类进行了大刀阔斧地改革,让多线程编程更加符合 C++ 语言的「面向对象」特性。
继承的本意是扩展基类的功能,所以继承 QThread 并把耗时操作代码塞入 run() 函数中的做法怎么看都感觉不伦不类。
2.现在的Qt多线程使用起来更加优雅
现在的我们可以直接将QThread这个类当作是程序开辟的新线程,加入其中的对象或者代码都是在一个新的时空中运行,QObject 中的 moveToThread()
函数可以在不破坏类结构的前提下使任何对象或者代码在新线程中运行。
假设现在我们有个 QObject 的子类 Worker,这个类有个成员函数 doSomething(),该函数中运行的代码非常耗时。此时我要做的就是将这个类对象“移动”到新线程里,这样 Worker 的所有成员函数就可以在新线程中运行了。意思就是这个Worker对象中的所有成员函数都是在一个新的线程运行
- 那么如何调用Worker对象的成员函数呢?
信号与槽机制的存在就是为了服务对象之间的通信。因此在主线程里需要有个 signal 信号来关联并触发 Worker 的槽函数,与此同时 Worker 类中也应该有个 signal 信号用于向外界发送运行的结果。
3.实例代码
[参考资料]https://zhuanlan.zhihu.com/p/53270619
作者原文中有详细代码及注释
- 需要注意的点有Worker这个对象不能给它设置
parent指针
,就用默认的nullptr
- 如果使用多个线程(线程池),需要n个线程时就需要有n个Worker加入到n个QThread对象中, 所以应该理解一个QThread对象就是一个崭新的线程,拥有独立的线程号.
- 别忘了配置好后一定要QThread->start()才能正常运行;
- 4个线程的线程池示例:
std::vector<ReadThread*> readHandle; /// 待放入线程中的句柄类
std::vector<QThread*> threadPool; /// 线程池
/// 解决信号与槽中传递自定义类型不能识别的bug
#include <QMetaType>
qRegisterMetaType<NodeInfo>("NodeInfo");
/// 开辟4个线程和4个要放入线程里的句柄类
for (int i = 0; i < THREAD_NUM; ++i) {
readHandle.push_back(new ReadThread());
threadPool.push_back(new QThread());
}
注意创建线程池threadPool
时不要用resize(4, new QThread())
的方式一次性创建,resize
第二个参数只会执行一次即构造一个QThread指针,导致threadPool
中4个内容其实都是同一个线程
for (int i = 0; i < THREAD_NUM; ++i) {
/// 依次放入线程中
readHandle[i]->moveToThread(threadPool[i]);
/// 将句柄类计算出的数据都链接到本对象中的同一个槽函数中
connect(readHandle[i], SIGNAL(sendResult(NodeInfo)), this, SLOT(recvFile(NodeInfo)));
connect(threadPool[i], SIGNAL(finished()), readHandle[i], SLOT(deleteLater()));
/// 开启, 必不可少
threadPool[i]->start();
}
/// 将当前信号连接到对应的句柄类, runNewThread1是开启新线程的信号
connect(this, SIGNAL(runNewThread1(QString)), readHandle[0], SLOT(readJoint(QString)));
connect(this, SIGNAL(runNewThread2(QString)), readHandle[1], SLOT(readJoint(QString)));
connect(this, SIGNAL(runNewThread3(QString)), readHandle[2], SLOT(readJoint(QString)));
connect(this, SIGNAL(runNewThread4(QString)), readHandle[3], SLOT(readJoint(QString)));