qDebug() << currentThreadId();
从 dbzhang800 的博客中转载两篇关于事件循环的文章,放在一起,写作备忘。
再次提到的一点是:事件循环和线程没有必然关系。 QThread 的 run() 方法始终是在一个单独线程执行的,但只有在 run() 方法中使用了 exec() 才真正开启了一个单独的事件循环。
顺便一提,模态对话框和事件循环也没有必然关系,可以转读这篇文章 QDialog 模态对话框与事件循环。
以下内容转载自 dbzhang800 的博客:
「QThread 的使用方法」
原文地址:http://hi.baidu.com/cyclone/blog/item/5fac3bc7ab1b90d1d10060f2.html
「QThread 使用探讨」
原文地址:http://hi.baidu.com/cyclone/blog/item/a33794ee00acba262cf53442.html
QThread 的使用方法
起源
昨天不小心看到Qt开发人员( Bradley T. Hughes)Blog中的一片文章 you are-doing-it-wrong 。 结果看得头昏脑胀:好歹也自学了近1年的Qt,也一直很小心、很认真地阅读Qt和manual和例子等资料,却被突然告知,QThread的正确使用方法 是一种自己从没见过,而且Qt manual、example、书籍中都没有提到过的一种方法。到底怎么了...
莫非manual、exmaple以及资料中的介绍都是错的??
认真看看其他的人的评论,总算理清了一点头绪。所有事情源于 QThread 的事件循环!
QThread 的两种使用方法
1. 不使用事件循环。这是官方的 Manual 、example 以及相关书籍中都介绍的一种的方法。
a. 子类化 QThread
b. 重载 run 函数,run函数内有一个 while 或 for 的死循环
c. 设置一个标记为来控制死循环的退出。
2. 使用事件循环。(博客 you are-doing-it-wrong 批驳的就是这种情况下的 一种用法。)
a. 子类化 QThread,
b. 重载 run 使其调用 QThread::exec()
c. 并为该类定义信号和槽,这样一来,由于槽函数并不会在新开的 thread 运行,很多人为了解决这个问题在构造函数中调用
moveToThread(this)
而争论和不解正是这样的一条语句造成的。
Bradley T. Hughes 给出说明是: QThread 应该被看做是操作系统线程的接口或控制点,而不应该包含需要在新线程中运行的代码。需要运行的代码应该放到一个QObject的子类中,然后将该子类的对象moveToThread到新线程中。
另外
在Qt4.3(包括)之前,run 是虚函数,必须子类化QThread来实现run函数。
而从Qt4.4开始,qthreads-no-longer-abstract ,run 默认调用 QThread::exec() 。这样一来不需要子类化 QThread 了,只需要子类化一个 QObject 就够了,这正是被 Bradley T. Hughes推荐的方法。
终于看懂了,但不管怎么说,都应该是 QThread 当初的设计导致的这种问题,而所有文档和例子中都没有提到该如何使用Qthread 进一步加剧了对QThread的这种误用。
QThread 使用探讨
QThread 似乎是很难的一个东西,特别是信号和槽,有非常多的人(尽管使用者本人往往不知道)在用不恰当(甚至错误)的方式在使用 QThread,随便用google一搜,就能搜出大量结果出来。无怪乎Qt的开发人员 Bradley T. Hughes 声嘶力竭地喊you are-doing-it-wrong。
和众多用户一样,初次看到这个时,感到 Bradley T. Hughes有 些莫名奇妙,小题大作。尽管不舒服,当时还是整理过一篇博客QThread 的使用方法
时间过去3个月,尽管依然没怎么用thread;但今天csdn论坛中有人问到这个问题,想想还是尽我所能整理一下吧。提升自己,方便他人,何乐而不为呢?
QThread东西还是比较多的,而且我对底层对象了解有限,仅就一点进行展开(或许是大家最关心的一点):QThread中的slots在那个线程中执行?
QThread::run
run 函数是做什么用的?Manual中说的清楚:
- run 对于线程的作用相当于main函数对于应用程序。它是线程的入口,run的开始和结束意味着线程的开始和结束。
原文如下(这段话我们称为定理一吧):
- The run() implementation is for a thread what the main() entry point is for the application. All code executed in a call stack that starts in the run() function is executed by the new thread, and the thread finishes when the function returns.
这么短的文字一眼就看完了,可是,这是什么意思呢?又能说明什么问题呢?看段简单代码:
class Thread:public QThread
{
Q_OBJECT
public:
Thread(QObject* parent=0):QThread(parent){}
public slots:
void slot() { ... }
signals:
void sig();
protected:
void run() { ...}
};
int main(int argc, char** argv)
{
...
Thread thread;
...
}
对照前面的定理,run函数中的代码时确定无疑要在次线程中运行的,那么其他的呢?比如 slot 是在次线程还是主线程中运行?
你想说主线程,但又心有不甘,对么?
QObject::connect
涉及信号槽,我们就躲不过 connect 函数,只是这个函数大家太熟悉。我不好意思再用一堆废话来描述它,但不说又不行,那么折中一下,只看它的最后一个参数吧(为了简单起见,只看它最常用的3个值)
下面的列表,我们暂称为定理二:
- 自动连接(Auto Connection)
- 这是默认设置
- 如果信号在接收者所依附的线程内发射,则等同于直接连接
- 如果发射信号的线程和接受者所依附的线程不同,则等同于队列连接
- 也就是这说,只存在下面两种情况
- 直接连接(Direct Connection)
- 当信号发射时,槽函数将直接被调用。
- 无论槽函数所属对象在哪个线程,槽函数都在发射信号的线程内执行。
- 队列连接(Queued Connection)
- 当控制权回到接受者所依附线程的事件循环时,槽函数被调用。
- 槽函数在接收者所依附线程执行。
同前面一样,这些文字大家都能看懂。但含义呢?
不妨继续拿前面的例子来看,slot 函数是在主线程还是次线程中执行呢?
定理二强调两个概念:发送信号的线程 和 接收者所依附的线程。而 slot 函数属于我们在main中创建的对象 thread,即thread依附于主线程
- 队列连接告诉我们:槽函数在接受者所依附线程执行。即 slot 将在主线程执行
- 直接连接告诉我们:槽函数在发送信号的线程执行。信号在那个线程发送呢??不定!
- 自动连接告诉我们:二者不同,等同于队列连接。即 slot 在主线程执行
太绕了?不是么(要彻底理解这几句话,你可能需要看Qt meta-object系统和Qt event系统)
怎么办呢?
如果上两节看不懂,就记住下面的话吧(自己总结的,用词上估计会不太准确)。
- QThread 是用来管理线程的,它所依附的线程和它管理的线程并不是同一个东西
- QThread 所依附的线程,就是执行 QThread t(0) 或 QThread * t=new QThread(0) 的线程。也就是咱们这儿的主线程
- QThread 管理的线程,就是 run 启动的线程。也就是次线程
- 因为QThread的对象依附在主线程中,所以他的slot函数会在主线程中执行,而不是次线程。除非:
- QThread 对象依附到次线程中(通过movetoThread)
- slot 和信号是直接连接,且信号在次线程中发射
- 但上两种解决方法都不好,因为QThread不是这么用的(Bradley T. Hughes)
好了,不再添加更多文字了,看代码,估计咱们都会轻松点
主线程(信号)QThread(槽)
这是 Qt Manual 和 例子中普遍采用的方法。 但由于manual没说槽函数是在主线程执行的,所以不少人都认为它应该是在次线程执行了。
- 定义一个 Dummy 类,用来发信号
- 定义一个 Thread 类,用来接收信号
- 重载 run 函数,目的是打印 threadid
/*!
* \file main.cpp
*
* Copyright (C) 2010, dbzhang800
* All rights reserved.
*
*/
#include <QtCore/QCoreApplication>
#include <QtCore/QObject>
#include <QtCore/QThread>
#include <QtCore/QDebug>
class Dummy:public QObject
{
Q_OBJECT
public:
Dummy(){}
public slots:
void emitsig()
{
emit sig();
}
signals:
void sig();
}; class Thread:public QThread
{
Q_OBJECT
public:
Thread(QObject* parent=0):QThread(parent)
{
//moveToThread(this);
}
public slots:
void slot_main()
{
qDebug()<<"from thread slot_main:" <<currentThreadId();
}
protected:
void run()
{
qDebug()<<"thread thread:"<<currentThreadId();
exec();
}
}; #include "main.moc" int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"main thread:"<<QThread::currentThreadId();
Thread thread;
Dummy dummy;
QObject::connect(&dummy, SIGNAL(sig()), &thread, SLOT(slot_main()));
thread.start();
dummy.emitsig();
return a.exec();
}
然后看到结果(具体值每次都变,但结论不变)
main thread: 0x1a40 from thread slot_main: 0x1a40 thread thread: 0x1a48
看到了吧,槽函数的线程和主线程是一样的!
如果你看过Qt自带的例子,你会发现 QThread 中 slot 和 run 函数共同操作的对象,都会用QMutex锁住。为什么?
因为slot和run处于不同线程,需要线程间的同步!
如果想让槽函数slot在次线程运行(比如它执行耗时的操作,会让主线程死掉),怎么解决呢?
- 注意:dummy信号是在主线程发射的, 接收者 thread 也在主线程中。
- 参考我们前面的结论,很容易想到:
- 将 thread 依附的线程改为次线程不就行了?
- 这也是代码中注释掉的 moveToThread(this)所做的,去掉注释,你会发现slot在次线程中运行
main thread: 0x13c0
thread thread: 0x1de0
from thread slot_main: 0x1de0
这可以工作,但这是 Bradley T. Hughes 强烈批判的用法。推荐的方法后面会给出。
run中信号与QThread中槽
- 定义一个 Dummy 类,在run中发射它的信号
- 也可以在run中发射 Thread 类中的信号,而不是Dummy(效果完全一样)
- QThread 定义槽函数,重载run函数
/*!
* \file main.cpp
*
* Copyright (C) 2010, dbzhang800
* All rights reserved.
*
*/
#include <QtCore/QCoreApplication>
#include <QtCore/QObject>
#include <QtCore/QThread>
#include <QtCore/QDebug> class Dummy:public QObject
{
Q_OBJECT
public:
Dummy(QObject* parent=0):QObject(parent){}
public slots:
void emitsig()
{
emit sig();
}
signals:
void sig();
}; class Thread:public QThread
{
Q_OBJECT
public:
Thread(QObject* parent=0):QThread(parent)
{
//moveToThread(this);
}
public slots:
void slot_thread()
{
qDebug()<<"from thread slot_thread:" <<currentThreadId();
}
signals:
void sig();
protected:
void run()
{
qDebug()<<"thread thread:"<<currentThreadId();
Dummy dummy;
connect(&dummy, SIGNAL(sig()), this, SLOT(slot_thread()));
dummy.emitsig();
exec();
}
}; #include "main.moc" int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"main thread:"<<QThread::currentThreadId();
Thread thread;
thread.start();
return a.exec();
}
想看结果么?
main thread: 0x15c0
thread thread: 0x1750
from thread slot_thread: 0x15c0
- 其实没悬念,肯定是主线程
- thread 对象本身在主线程。所以它的槽也在要在主线程执行
如何解决呢?
- (方法一)前面提了 moveToThread,这儿可以用,而且可以解决问题。当同样,是被批判的对象。
- (方法二)注意哦,这儿我们的信号时次线程发出的,对比connect连接方式,会发现:
- 采用直接连接,槽函数将在次线程(信号发出的线程)执行
- 这个方法不太好,因为你需要处理slot和它的对象所在线程的同步。需要 QMutex 一类的东西
推荐的方法
千呼万唤始出来。
其实,这个方法太简单,太好用了。定义一个普通的QObject派生类,然后将其对象move到QThread中。使用信号和槽时根本不用考虑多线程的存在。也不用使用QMutex来进行同步,Qt的事件循环会自己自动处理好这个。
/*!
* \file main.cpp
*
* Copyright (C) 2010, dbzhang800
* All rights reserved.
*
*/
#include <QtCore/QCoreApplication>
#include <QtCore/QObject>
#include <QtCore/QThread>
#include <QtCore/QDebug> class Dummy:public QObject
{
Q_OBJECT
public:
Dummy(QObject* parent=0):QObject(parent) {}
public slots:
void emitsig()
{
emit sig();
}
signals:
void sig();
}; class Object:public QObject
{
Q_OBJECT
public:
Object(){}
public slots:
void slot()
{
qDebug()<<"from thread slot:" <<QThread::currentThreadId();
}
}; #include "main.moc" int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"main thread:"<<QThread::currentThreadId();
QThread thread;
Object obj;
Dummy dummy;
obj.moveToThread(&thread);
QObject::connect(&dummy, SIGNAL(sig()), &obj, SLOT(slot()));
thread.start();
dummy.emitsig();
return a.exec();
}
结果:恩,slot确实不在主线程中运行(这么简单不值得欢呼么?)
main thread: 0x1a5c
from thread slot: 0x186c
其他
- 本文只考虑了使用事件循环的情况,也有可能run中没有事件循环。这时信号与槽会与本文有点差别。比如run中使用connect时,队列连接就受限制了。其实只要理解了前面这些,没有事件循环的情况很容易就想通了。
qDebug() << currentThreadId();的更多相关文章
- Qt Disable QDebug And Warning Output
如何禁止qDebug的输出 在项目开发的过程中,为了开发方便,我们常常在Qt的Application Output中输出一些内容,慢慢的. 有些qDebug就会被我们遗忘再角落里. 虽然对整个程序影响 ...
- QT屏蔽qDebug的方法
在工程文件.pro里面添加 DEFINES += QT_NO_WARNING_OUTPUT\ QT_NO_DEBUG_OUTPUT 然后rebuild all.这样 ...
- VS2010中qDebug输出乱码的问题
1.开发环境:安装Qt5.3.2(离线安装包安装):VS版本为:2010 SP1Rel:源代码默认保存格式为GB2312. 2.输出乱码的代码 #include <QtCore/QCoreApp ...
- Qt:禁止qDebug的输出
Qt:禁止qDebug的输出 在工程的.pro文件里加上以下编译批令即可: DEFINES += QT_NO_DEBUG_OUTPUT
- Qt QDebug :Cannot retrieve debugging output!
调试Qt程序时用Qdebug类输出调试信息: qDebug("read My Com"); 这个问题是个小问题,其实跟程序没关系.当你同时开多个Qt程序(Creator编程 ...
- qt中如果用qDebug输出彩色调试信息
Linux: 在终端输出彩色信息有点类似于html的语法,即在要输出的文字前加上转义字符. 指令格式如下\033[*m 这里的*就是转义字符,例如我们要输出一段绿色的文字 qDebug(" ...
- qDebug 学习小结
在qtcentre中看到有网友问这样一个问题: Why this doesn't work? qDebug() << "Test" << std::endl ...
- VS_QT中配置qDebug输出
在使用qt_create时可以使用qDebug进行调试输出.在VS中也可以使用.但需要配置.配置过程如下图所示: 一.首先右击工程名,选择最后一个选项“Properties” 二.然后选择Linker ...
- QDebug &operator<<出错(根据QString来找,是不得要领的,而是应该根据QString所在的对象来思考)
程序运行后,总是崩溃在这个地方:inline QDebug &operator<<(const QString & t) 我应该用什么办法找出是哪个QString出了问题呢 ...
随机推荐
- GIL锁、进程池与线程池、同步异步
GIL锁定义 GIL锁:Global Interpreter Lock 全局解释器 本质上是一把互斥锁 官方解释: 在CPython中,这个全局解释器锁,也称为GIL,是一个互斥锁,防止多个线程在同 ...
- 运动的border,仿当当简易效果
突然想到以前看到当当上有个效果,当鼠标移上去,图片边框是运动添加上的,还以为是css3或者是canvas做的呢,做完幽灵按钮后,才知道,so easy,只不过是animate+position的杰作 ...
- 库克谈新iPhone不支持5G的理由,学习一下如何高手怎么应答
库克谈新iPhone不支持5G的理由 投递人 itwriter 发布于 2019-09-12 08:41 评论(14) 有1623人阅读 原文链接 [收藏] « » 9 月 12 日消息,苹果公司 C ...
- nginx简单反向代理实例
一.要做什么? 实例最后实现的效果图: 我们在浏览器地址栏上输入 wangtong,代理服务器获取请求,将请求转发至指定的 tomcat 上 二.怎样做? 1.准备环境 虚拟中中需要安装 JDK+To ...
- Ubuntu系统---又显示nvidia-smi 未找到命令
Ubuntu系统---又显示nvidia-smi 未找到命令 本来nvidia驱动+CUDA安装好用,两次遇到开机发现字体异常,不用合计,是显卡驱动的问题.一查,确实是nvidia-smi 未找到命令 ...
- Windows10官方正版系统的安装、激活、升级、U盘制作,无毒无害无捆绑无风险教程
一般电脑系统出了其他问题或电脑用久太卡了,可以选择此类方法解决系统卡顿问题,重置电脑系统或也可以恢复出厂设置 如果出现重置找不到恢复环境问题 可以通过下载系统镜像来解决,进入 MSDN 网站下载所需系 ...
- 大数据之路week04--day05(java 正则表达式)
1.1 正则表达式 1.2 简介 Regular Expression 正则表达式 常简称为: regex.正则 正则表达式是一整套约束字符串的语法规则,独立于任何编程语言 正则表达式 方便.灵 ...
- 2019EC-Final参赛总结
本来想发知乎上的,后来发现太长就放这好了23333 没写过这种东西,所以写得比较混乱&流水账 以下内容均为我的个人视角XD 赛前 在车上的时候,红太阳跟我们说他头晕(虽然他好像每场比赛都头 ...
- [2019牛客多校第三场][G. Removing Stones]
题目链接:https://ac.nowcoder.com/acm/contest/883/G 题目大意:有\(n\)堆石头,每堆有\(a_i\)个,每次可以选其中两堆非零的石堆,各取走一个石子,当所有 ...
- ELK---- Elasticsearch 安装 学习
elk = 分布式系统收集管理多台服务器日志,并能快速做增删改查操作的几个工具集合简称. elasticsearch(存储日志)+logstash(收集日志)+kibana(展示数据) 在linux ...