QT多线程的设计(imxiangzi总结)
来源:互联网 发布:淘宝买东西后怎么评价 编辑:程序博客网 时间:2024/06/14 04:50
1. 不使用事件循环。这是官方的 Manual 、example 以及相关书籍中都介绍的一种的方法。
a. 子类化 QThread
b. 重载 run 函数,run函数内有一个 while 或 for 的死循环
c. 设置一个标记为来控制死循环的退出。
- void run()
- {
- while(bRun)//如果需要退出线程就将bRun设置为false.
- {
- qDebug()<<"run thread ID = "<<QThread::currentThreadId();
- QThread::usleep(0);//usleep的值设置为0一样会占满cpu,但是根据老大测试,并不会使程序变卡。你也可以设置为10.
- }
- // this->exec();//没加这个的话线程就会结束,并发出Finsh()信号
- }
如果使用这一方法,QThread::quit()没有效果。因为这个线程根本就不需要事件循环。这种情况想退出,将bRun设置为false或者直接使用QT很不推荐的terminate().
[如果想要通过exit和quit正常退出函数,则需要在run函数中添加exec函数]
2. 使用事件循环。(博客 you are-doing-it-wrong 批驳的就是这种情况下的 一种用法。)
a. 子类化 Thread,[有时候在构造函数中添加moveToThread(this)]
b. 重载 run 使其调用 QThread::exec()
c. 并为该类定义信号和槽,
这样一来,由于槽函数并不会在新开的 thread 运行,很多人为了解决这个问题在构造函数中调用
moveToThread(this)
而争论和不解正是这样的一条语句造成的。
Bradley T. Hughes 给出说明是: QThread 应该被看做是操作系统线程的接口或控制点,而不应该包含需要在新线程中运行的代码。需要运行的代码应该放到一个QObject的子类中,然后将该子类的对象moveToThread到新线程中。
这是 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 强烈批判的用法。推荐的方法后面会给出。
方法(3):
在Qt4.3(包括)之前,run 是虚函数,必须子类化QThread来实现run函数。
而从Qt4.4开始,qthreads-no-longer-abstract ,run 默认调用 QThread::exec() 。
这样一来不需要子类化 QThread 了,只需要子类化一个 QObject 就够了,这正是被 Bradley T. Hughes推荐的方法。
方法如下
QThread thread; Object obj; Dummy dummy; obj.moveToThread(&thread); QObject::connect(&dummy, SIGNAL(sig()), &obj, SLOT(slot())); thread.start();
具体的例子:
/*!* \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
==========================================
总结:
方法1和方法3是推荐使用的,
当有长时间的处理任务的时候,使用方法1,
当使用到信号和操的时候,使用方法3
==========================================
原因分析:
1.
现在我们来分析下connect()的五个参数。
有六种参数:
Qt::AutoConnection
Qt::DirectConnection
Qt::QueuedConnection
Qt::
BlockingQueuedConnectionQt::UniqueConnection
Qt::AutoCompatConnection
这里面一共有六种方式。
前两种比较相似,都是同一线程之间连接的方式,不同的是Qt::AutoConnection是系统默认的连接方式。这种方式连接的时候,槽不是马上被执行的,而是进入一个消息队列,待到何时执行就不是我们可以知道的了,当信号和槽不是同个线程,会使用第三种QT::QueueConnection的链接方式。如果信号和槽是同个线程,调用第二种Qt::DirectConnection链接方式。
第二种Qt::DirectConnection是直接连接,也就是只要信号发出直接就到槽去执行,无论槽函数所属对象在哪个线程,槽函数都在发射信号的线程内执行,一旦使用这种连接,槽将会不在线程执行!。
第三种Qt::QueuedConnection和第四种Qt::BlockingQueuedConnection是相似的,都是可以在不同进程之间进行连接的,不同的是,这里第三种是在对象的当前线程中执行,并且是按照队列顺序执行。当当前线程停止,就会等待下一次启动线程时再按队列顺序执行 ,等待QApplication::exec()或者线程的QThread::exec()才执行相应的槽,就是说:当控制权回到接受者所依附线程的事件循环时,槽函数被调用,而且槽函数在接收者所依附线程执行,使用这种连接,槽会在线程执行。
第四种Qt::BlockingQueuedConnection是(必须信号和曹在不同线程中,否则直接产生死锁)这个是完全同步队列只有槽线程执行完才会返回,否则发送线程也会等待,相当于是不同的线程可以同步起来执行。
第五种Qt::UniqueConnection跟默认工作方式相同,只是不能重复连接相同的信号和槽;因为如果重复链接就会导致一个信号发出,对应槽函数就会执行多次。
第六种Qt::AutoCompatConnection是为了连接QT4 到QT3的信号槽机制兼容方式,工作方式跟Qt::AutoConnection一样。显然这里我们应该选择第三种方式,我们不希望子线程没结束主线程还要等,我们只是希望利用这个空闲时间去干别的事情,当子线程执行完了,只要发消息给主线程就行了,到时候主线程会去响应。
两个概念:发送信号的线程 和 接收者所依附的线程。而 slot 函数属于我们在main中创建的对象 thread,即thread依附于主线程
- 队列连接告诉我们:槽函数在接受者所依附线程执行。即 slot 将在主线程执行
- 直接连接告诉我们:槽函数在发送信号的线程执行。信号在那个线程发送呢??不定!
- 自动连接告诉我们:二者不同,等同于队列连接。即 slot 在主线程执行
- QThread 是用来管理线程的,它所依附的线程和它管理的线程并不是同一个东西
- QThread 所依附的线程,就是执行 QThread t(0) 或 QThread * t=new QThread(0) 的线程。也就是咱们这儿的主线程
- QThread 管理的线程,就是 run 启动的线程。也就是次线程
- 因为QThread的对象依附在主线程中,所以他的slot函数会在主线程中执行,而不是次线程。除非:
- QThread 对象依附到次线程中(通过movetoThread)
- slot 和信号是直接连接,且信号在次线程中发射
参考:
在Qt使用moveToThread() qt的线程
http://blog.csdn.net/zhangbinsijifeng/article/details/47783599
QThread使用——关于run和movetoThread的区别
http://blog.csdn.net/imxiangzi/article/details/77482797
- QT多线程的设计(imxiangzi总结)
- Qt: Qt多线程设计
- qt多线程设计
- Qt多线程编程总结(一)
- Qt多线程编程总结(一)
- Qt多线程编程总结(一)
- [转] Qt多线程编程总结(一)
- Qt多线程编程总结(一)
- Qt多线程同步总结
- Qt多线程同步总结 .
- Qt多线程简单总结
- Qt多线程总结
- QT多线程使用总结
- Qt多线程同步总结
- QT界面(控件)相关设计的一些技巧总结
- QT界面(控件)相关设计的一些技巧总结
- QT界面(控件)相关设计的一些技巧总结
- QT界面(控件)相关设计的一些技巧总结
- 控件的Ontouch事件和onClick事件冲突
- zookeeper报错:Unable to start failover controller. Parent znode does not exist. Run with -formatZK fla
- DATAGUARD搭建步骤及遇到的问题
- 蓝牙调试
- 自定义控件之绘图篇:Paint之setXfermode(一)
- QT多线程的设计(imxiangzi总结)
- 使用JS代码实现点击按钮下载文件
- 0822Link
- 字符常见的几种编码方式
- Android Kotlin问题总结
- 常见 HTTP/FTP/WebSocket 错误代码大全
- 深入理解Swift 面向协议编程
- freeswitch配置
- 技术就能成就梦想?没有品牌效应能行的通吗?