QThread官方推荐使用方法

本文介绍Qt中使用QObject而非QThread实现多线程的方法。详细解释了如何利用QObject的moveToThread函数转移线程,避免QThread易犯错误,并通过官方示例展示了正确的多线程编程实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

现在Qt官方并不是很推荐继承 QThread来实现多线程方法,而是极力推崇继承 QObject的方法来实现,当然用哪个方法实现要视情况而定,别弄错了就行,估计Qt如此推崇继承 QObject的方法可能是 QThread太容易用错的原因。

继承QThread实现多线程的方法点此

1. 前言

上一篇介绍了传统的多线程使用方法——继承QThread来实现多线程,这也是很多框架的做法(MFC),但Qt还有一种多线程的实现方法,比直接继承QThread更为灵活,就是直接继承QObject实现多线程。

QObject是Qt框架的基本类,但凡涉及到信号槽有关的类都是继承于QObjectQObject是一个功能异常强大的类,它提供了Qt关键技术信号和槽的支持以及事件系统的支持,同时它提供了线程操作的接口,也就是QObject是可以选择不同的线程里执行的。

QObject的线程转移函数是:void moveToThread(QThread * targetThread) ,通过此函数可以把一个顶层Object(就是没有父级)转移到一个新的线程里。

QThread非常容易被新手误用,主要是QThread自身并不生存在它run函数所在的线程,而是生存在旧的线程中,此问题在上一篇重点描述了。由于QThread的这个特性,导致在调用QThread的非run函数容易在旧线程中执行,因此人们发现了一个新的魔改QThread的方法: 
人们发现,咦,QThread也继承QObjectQObject有个函数void moveToThread(QThread * targetThread)可以把Object的运行线程转移,那么:(下面是非常不推荐的魔改做法,别用此方法):


  1. class MyThread : public QThread{
  2. public:
  3. MyThread ()
  4. {
  5. moveToThread(this);
  6. }
  7. ……
  8. };

直接把MyThread整个转移到MyThread的新线程中,MyThread不仅run,其它函数也在新线程里了。这样的确可以运行正常,但这并不是QThread设计的初衷,Qt还专门发过一篇文章来吐槽这个做法。

在Qt4.8之后,Qt多线程的写法最好还是通过QObject来实现,和线程的交互通过信号和槽(实际上其实是通过事件)联系。

2.继承QObject的多线程实现

QObject来实现多线程有个非常好的优点,就是默认就支持事件循环(Qt的许多非GUI类也需要事件循环支持,如QTimerQTcpSocket),QThread要支持事件循环需要在QThread::run()中调用QThread::exec()来提供对消息循环的支持,否则那些需要事件循环支持的类都不能正常发送信号,因此如果要使用信号和槽,那就直接使用QObject来实现多线程。

2.1 创建及销毁线程

继承QObject多线程的方法线程的创建很简单,只要让QThreadstart函数运行起来就行,但是需要注意销毁线程的方法  在线程创建之后,这个QObject的销毁不应该在主线程里进行,而是通过deleteLater槽进行安全的销毁,因此,继承QObject多线程的方法在创建时有几个槽函数需要特别关注:

  • 一个是QThreadfinished信号对接QObjectdeleteLater使得线程结束后,继承QObject的那个多线程类会自己销毁
  • 另一个是QThreadfinished信号对接QThread自己的deleteLater,这个不是必须,下面官方例子就没这样做

看看Qt官方文档的例子:


  1. class Worker : public QObject
  2. {
  3. Q_OBJECT
  4. public slots:
  5. void doWork(const QString &parameter) {
  6. QString result;
  7. /* ... here is the expensive or blocking operation ... */
  8. emit resultReady(result);
  9. }
  10. signals:
  11. void resultReady(const QString &result);
  12. };
  13. class Controller : public QObject
  14. {
  15. Q_OBJECT
  16. QThread workerThread;
  17. public:
  18. Controller() {
  19. Worker *worker = new Worker;
  20. worker->moveToThread(&workerThread);
  21. connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
  22. connect(this, &Controller::operate, worker, &Worker::doWork);
  23. connect(worker, &Worker::resultReady, this, &Controller::handleResults);
  24. workerThread.start();
  25. }
  26. ~Controller() {
  27. workerThread.quit();
  28. workerThread.wait();
  29. }
  30. public slots:
  31. void handleResults(const QString &);
  32. signals:
  33. void operate(const QString &);
  34. };

使用QObject创建多线程的方法如下:

  • 写一个继承QObject的类,对需要进行复杂耗时逻辑的入口函数声明为槽函数
  • 此类在旧线程new出来,不能给它设置任何父对象
  • 同时声明一个QThread对象,在官方例子里,QThread并没有new出来,这样在析构时就需要调用QThread::wait(),如果是堆分配的话, 可以通过deleteLater来让线程自杀
  • 把obj通过moveToThread方法转移到新线程中,此时object已经是在线程中了
  • 把线程的finished信号和object的deleteLater槽连接,这个信号槽必须连接,否则会内存泄漏
  • 正常连接其他信号和槽(在连接信号槽之前调用moveToThread,不需要处理connect的第五个参数,否则就显示声明用Qt::QueuedConnection来连接
  • 初始化完后调用'QThread::start()'来启动线程
  • 在逻辑结束后,调用QThread::quit退出线程的事件循环

使用QObject来实现多线程比用继承QThread的方法更加灵活,整个类都是在新的线程中,通过信号槽和主线程传递数据

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值