文章目录

  • 1. 引言
  • 2. QThread 文档
  • 3. QThread::run 和 QObject::connect
  • 4. QObject::moveToThread()
  • 5. 使用场景
    • 对于子类化 Thread 的方式
    • 对于 worker move to thread 的方式

1. 引言

你会用QThread吗?有几种使用方式?这几种使用方式都在何种场景下使用?有什么需要注意的地方吗?

2. QThread 文档

上来先看 Qt 帮助文档。QThread Class 文档,详细描述的第一句话:

The QThread class provides a platform-independent way to manage threads

注意看倒数第二个单词,QThread 不等于线程,QThread 是负责管理线程的。
接下来看文档,我们清楚的知道QThread的两种使用方式。

方式一:子类化QThread,并重新实现 run() 函数
方式二:定义工作对象继承自 QObject,然后把这个工作对象move到QThread的一个对象中。

3. QThread::run 和 QObject::connect

先看下帮助文档上怎么描述这个 QThread::run 函数的:
The starting point for the thread. After calling start(), the newly created thread calls this function. The default implementation simply calls exec().
You can reimplement this function to facilitate advanced thread management. Returning from this method will end the execution of the thread.
这段英文描述的很清楚。好,下面看例子:

先看文档上的例子:

class Worker : public QObject
{Q_OBJECT
public:Worker(){}
public slots:void emitsig(){emit sig();}
signals:void sig();
};class Thread : public QThread
{Q_OBJECT
public:Thread(QObject* parent=0):QThread(parent){}void fun(){qDebug() << "Thread::fun threadID: " << QThread::currentThreadId();}
public slots:void slotFun(){qDebug() << "Thread::slotFun threadID: " << QThread::currentThreadId();}
signals:void sig();
protected:void run(){QThread::sleep(5);qDebug() <<"Thread::run threadID: " << currentThreadId();fun();slotFun();exec();qDebug() << "Thread:: exit";}
};int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);Thread thread;Worker work;QObject::connect(&work, SIGNAL(sig()), &thread, SLOT(slotFun()));thread.start();qDebug() << "Main threadID: " << QThread::currentThreadId();thread.fun();thread.slotFun();qDebug() << "before signal. threadID: " <<  QThread::currentThreadId();work.emitsig();qDebug() << "after signal. threadID: " <<  QThread::currentThreadId();return a.exec();
}

此处不考虑线程资源回收问题。请问打印输出的结果是什么?线程ID是否一样呢?

公布结果之前,先看下关于 QObject::connect 函数相关说明:

涉及信号槽,我们就躲不过 connect 函数,只是这个函数大家太熟悉。我不好意思再用一堆废话来描述它,但不说又不行,那么折中一下,只看它的最后一个参数吧(为了简单起见,只看它最常用的3个值):

  • 自动连接(Auto Connection)

    • 这是默认设置
    • 如果信号在接收者所依附的线程内发射,则等同于直接连接
    • 如果发射信号的线程和接受者所依附的线程不同,则等同于队列连接
    • 也就是这说,只存在下面两种情况(直接连接和队列连接)
  • 直接连接(Direct Connection)
    • 当信号发射时,槽函数将直接被调用。
    • 无论槽函数所属对象在哪个线程,槽函数都在发射信号的线程内执行。
  • 队列连接(Queued Connection)
    • 当控制权回到接受者所依附线程的事件循环时,槽函数被调用。
    • 槽函数在接收者所依附线程执行。

看了这些说明之后,你对刚才脑子里的结果是否确认呢?
下面公布结果:

Main threadID:  0x3318
Thread::fun threadID:  0x3318
Thread::slotFun threadID:  0x3318
before signal. threadID:  0x3318
Thread::slotFun threadID:  0x3318
after signal. threadID:  0x3318
Thread::run threadID:  0x3e30
Thread::fun threadID:  0x3e30
Thread::slotFun threadID:  0x3e30

为什么会有这样的结果呢?

因为:

  • QThread 是用来管理线程的,它所依附的线程和它管理的线程并不是同一个东西
  • QThread 所依附的线程,就是执行 QThread t(0) 或 QThread * t=new QThread(0) 的线程。也就是咱们这儿的主线程
  • QThread 管理的线程,就是 run 启动的线程。也就是次线程
    • 因为 QThread 的对象依附在主线程中,所以他的 slot 函数会在主线程中执行,而不是次线程。除非:QThread 对象依附到次线程中(通过movetoThread)
    • slot 和信号是直接连接,且信号在次线程中发射

槽函数呢,其实就可以当成普通函数来使用。相信大家看完输出结果和原因分析之后,应该能理解为什么是这样的输出了。

下面再看一个例子:

class Thread : public QThread
{Q_OBJECT
private slots:void onTimeout(){qDebug() << "Thread::onTimeout get called from? : " << QThread::currentThreadId();}private:void run(){qDebug() << "From worker thread: " << currentThreadId();QTimer timer;connect(&timer, SIGNAL(timeout()), this, SLOT(onTimeout()));timer.start(1000);exec();}
};int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);qDebug() << "Main threadID: " << QThread::currentThreadId();Thread thread;thread.start();return a.exec();
}

看下打印结果:

Main threadID:  0x2e78
From worker thread:  0x2998
Thread::onTimeout get called from? :  0x2e78
Thread::onTimeout get called from? :  0x2e78
Thread::onTimeout get called from? :  0x2e78
Thread::onTimeout get called from? :  0x2e78
Thread::onTimeout get called from? :  0x2e78
Thread::onTimeout get called from? :  0x2e78
Thread::onTimeout get called from? :  0x2e78
Thread::onTimeout get called from? :  0x2e78
Thread::onTimeout get called from? :  0x2e78
...

是不是跟我们心里预期的不一样?我想让一些耗时操作放到 onTimeOut 里面去处理,并且由子线程来处理啊,怎么执行在主线程里?

因为 this 指的是Thread,因为 connect 最后一个参数是自动连接,信号发射者和接收者所依附的线程不同,所以是队列连接。那要怎么改呢?
connect(&timer, SIGNAL(timeout()), this, SLOT(onTimeout()), Qt::DirectConnection);
但其实这也是不好的实现方式,因为 onTimeout 是 Thread 对象的成员函数,但却被它所创建的子线程调用。所以我们引出第二种使用方式。

如果把例子中 Thread 的 run 函数里面的 exec() 去掉会怎样呢?

4. QObject::moveToThread()

再来对上一个例子修改一下:

class Worker : public QObject
{Q_OBJECT
public slots:void onTimeout(){qDebug() << "Worker::onTimeout get called from?: " << QThread::currentThreadId();}
};int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);qDebug() << "From main thread: " << QThread::currentThreadId();QThread thread;QTimer timer;Worker worker;QObject::connect(&timer, SIGNAL(timeout()), &worker, SLOT(onTimeout()));timer.start(1000);worker.moveToThread(&thread);thread.start();return a.exec();
}

这样就是我们想要的方式了。来看下打印结果:

From main thread:  0x1238
Worker::onTimeout get called from?:  0x2bb0
Worker::onTimeout get called from?:  0x2bb0
Worker::onTimeout get called from?:  0x2bb0
Worker::onTimeout get called from?:  0x2bb0
Worker::onTimeout get called from?:  0x2bb0

5. 使用场景

既然两种方式都可以使用,哪是不是随便使用其中一种方式就可以了呢?我们还是要具体问题,具体分析。哪个合适使用哪个。就像之前说过的,C 语言也能实现面向对象,但是 C++ 实现起来更快更方便而已。

对于子类化 Thread 的方式

这种方式适用于一些任务场景:

  • 很多经典线程问题(生产者,消费者等)
  • 独立不依赖的一些工作任务

这种方式有一些特点:

  • 不需要事件循环,一次性的执行
  • 不需要被调用槽函数
  • 可以自己定义 run() 函数的实现

这种方式有一些陷阱:

  • 提供槽函数,子类化对象是属于主线程的,又没有事件循环,所以槽函数会被主线程执行
  • 调用 moveToThread(this)

对于 worker move to thread 的方式

这种方式适用于一些任务场景:

  • 内部独立型的一些任务
  • “管理”任务

这种方式有一些特点:

  • 事件驱动型
  • 需要和外部进行通信
  • 没有一个单一的入口点

这种方式有一些陷阱:

  • 这个任务到底需不需要事件驱动?
  • 处理太耗时,而不把权利交给事件循环
  • 假的事件循环,入口点一直不退出,不能把权利交给事件循环

QThread资料

QThread 的使用相关推荐

  1. Python Qt GUI设计:QTimer计时器类、QThread多线程类和事件处理类(基础篇—8)

    目录 1.QTimer计时器类 2.QThread多线程类 3.事件处理类 一般情况下,应用程序都是单线程运行的,但是对于GUI程序来说,单线程有时候满足不了需求.例如,如果需要执行一个特别耗时的操作 ...

  2. pyqt5 使用 QTimer, QThread, pyqtSignal 实现自动执行,多线程,自定义信号触发。

    渣渣用法,请等待我心情好的时候更新. 1.第一个例子 1.1 先看mainwindow.py from PyQt5 import QtCore, QtGui, QtWidgetsclass Ui_Ma ...

  3. Qt中使用多线程的一些心得(一)——继承QThread的多线程使用方法

    一 前言 二Qt多线程方法一 继承QThread 2.1使用多线程的原因分析 2.2 写一个继承于QThread的线程 三 总结 一 前言   本篇文章部分内容参考了该博文:传送门.   Qt中有两种 ...

  4. QThread中的互斥、读写锁、信号量、条件变量

    该文出自:http://www.civilnet.cn/bbs/browse.php?topicno=78431 在gemfield的<从pthread到QThread>一文中我们了解了线 ...

  5. qthread destroyed while thread is still running

    qthread destroyed while thread is still running 然后报错(windows下):QThread: Destroyed while thread is st ...

  6. QThread: Destroyed while thread is still running

    QThread: Destroyed while thread is still running 原因:python中线程不能是临时变量, 错误代码: for cam in cams:mdcall_t ...

  7. PyQt5之QThread线程模块

    PyQt5之QThread线程模块 版权声明:版权没有,转载随意 https://blog.csdn.net/MAOZEXIJR/article/details/80983337 一.QThread ...

  8. 在Qt(C++)中使用QThread实现多线程

    1. 引言 多线程对于需要处理耗时任务的应用很有用,一方面响应用户操作.更新界面显示,另一方面在"后台"进行耗时操作,比如大量运算.复制大文件.网络传输等. 使用Qt框架开发应用程 ...

  9. 重点:怎样正确的使用QThread类(注:包括推荐使用QThread线程的新方法QObject::moveToThread)...

    背景描述: 以前,继承 QThread 重新实现 run() 函数是使用 QThread唯一推荐的使用方法.这是相当直观和易于使用的.但是在工作线程中使用槽机制和Qt事件循环时,一些用户使用错了.Qt ...

  10. QThread使用——关于run和movetoThread的区别

    QThread 使用探讨 2010-10-23 00:30 注意:本文停止更新,请优先考虑 Qt 线程基础(QThread.QtConcurrent等) dbzhang800 2011.06.18 Q ...

最新文章

  1. nginx+keepalived高可用web架构
  2. js中对于数据类型的一些理解
  3. basic和python_PythonBasic
  4. 矩阵位移法matlab编程,矩阵位移法_MATLAB_GUI.doc
  5. 加密安装Kli Linux
  6. treeselect 如何选中多个_转pdf后出现word没有的形状——对象选择窗格(同时选择多个形状、图形)...
  7. 从C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\mt.exe返回错误
  8. 数学速算法64种口诀_小学数学有哪些数学计算技巧?
  9. android activity 主题,android activity 主题
  10. MATLAB的官方网站上其实有很多MATLAB的学习和使用资料(文档、视频都有不少)
  11. 第一天mysq踩坑--ERROR 1820 (HY000): You must reset your password using ALTER USER statement before execut
  12. [转载]类名.this与类名.class_-Chaz-_新浪博客
  13. apache实验报告 linux_apache服务器的安装与配置实验报告.doc
  14. 〈四〉ElasticSearch的认识:基础原理的补充
  15. 冰雪之冠上的明珠与东方明珠 辉映在黄浦江两岸
  16. Perfmon - Windows 自带系统监控工具
  17. 物理服务器与云服务器的区别
  18. 电影院同场不同价 你愿意为“黄金座位”买单吗
  19. 用Biome-BGC模型如何模拟水循环过程
  20. 你相信进化吗?探索通用人工智能的重要途径 | 算法观点

热门文章

  1. webservice 框架比较
  2. 计算机取证程序论文,计算机取证论文参考文献推荐 计算机取证论文参考文献哪里找...
  3. 专家教你简单又轻松的MD5解密方法,一看就会
  4. Photoshop插件-黑白(一)-脚本开发-PS插件
  5. Linux中访问tomcat方式,linux软连接_Linux中为Tomcat启用软链接的三种方式
  6. 新浪股票数据接口获取
  7. 解只含加减的一元一次方程
  8. c++实现吃豆子游戏
  9. 中标麒麟 V7 操作系统安装达梦数据库 DM8
  10. laravel框架简单总结