Qt多线程和GUI界面假死(run()是线程的入口,就像main()对于应用程序的作用。分析QThread::exec函数的源码,旧的QMutexLocker模式其实很好用,挡住别人进入抢占资源,可照抄)good
QThread的常见特性:
run()是线程的入口,就像main()对于应用程序的作用。QThread中对run()的默认实现调用了exec(),从而创建一个QEventLoop对象,由其处理该线程事件队列(每一个线程都有一个属于自己的事件队列)中的事件。简单用代码描述如下:

1 int QThread::exec()
2 {
3 //...
4 QEventLoop eventLoop;
5 int returnCode = eventLoop.exec();
6 //...
7 return returnCode;
8 }
9
10 int QEventLoop::exec(ProcessEventsFlags flags)
11 {
12 //...
13 while (!d->exit) {
14 while (!posted_event_queue_is_empty) {
15 process_next_posted_event();
16 }
17 }
18 //...
19 }

由此可见,exec()在其内部不断做着循环遍历事件队列的工作,调用QThread的quit()或exit()方法使停止工作,尽量不要使用terminate(),该方法过于粗暴,造成资源不能释放,甚至互斥锁还处于加锁状态。
1.旧的使用方式:
这是qt4.6及之前的使用方法,这种方式本没有什么错误,可以处理我们的绝大多数需求。Thread对象本身工作在主线程下,即使调用的t.stop()方法,它也是工作在主线程下,只有run()范围内的代码工作在次线程中。

1 class WorkerThread : public QThread
2 {
3 Q_OBJECT
4
5 void run() Q_DECL_OVERRIDE {
6 QString result;
7 emit resultReady(result); // 只发射信号,去真正的业务内容
8 }
9
10 signals:
11 void resultReady(const QString &s);
12 };
13
14 void MyObject::startWorkInAThread()
15 {
16 WorkerThread *workerThread = new WorkerThread(this);
17 connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
18 connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
19 workerThread->start();
20 }

从Qt4.8起,可以释放运行刚刚结束的线程对象,通过连接finished()信号到QObject::deleteLater()。
2.推荐的使用方式:
这是Qt4.7及以后版本推荐的工作方式。其主要特点就是利用Qt的事件驱动特性,将需要在次线程中处理的业务放在独立的模块(类)中,由主线程创建完该对象后,将其移交给指定的线程,且可以将多个类似的对象移交给同一个线程。在这个例子中,信号由主线程的QTimer对象发出,之后Qt会将关联的事件放到worker所属线程的事件队列。由于队列连接的作用,在不同线程间连接信号和槽是很安全的。
说说connect最后一个参数,连接类型:
1)自动连接(AutoConnection),默认的连接方式,如果信号与槽,也就是发送者与接受者在同一线程,等同于直接连接;如果发送者与接受者处在不同线程,等同于队列连接。
2)直接连接(DirectConnection),当信号发射时,槽函数立即直接调用。无论槽函数所属对象在哪个线程,槽函数总在发送者所在线程执行。
3)队列连接(QueuedConnection),当控制权回到接受者所在线程的事件循环时,槽函数被调用。槽函数在接受者所在线程执行。

1 class Worker : public QObject
2 {
3 Q_OBJECT
4
5 public slots:
6 void doWork(const QString meter) {
7 // ...
8 emit resultReady(result);
9 }
10 void stopWork(){
11 //...
12 }
13 signals:
14 void resultReady(const QString &result);
15 };
16
17 class Controller : public QObject
18 {
19 Q_OBJECT
20
21 QThread workerThread;
22
23 public:
24 Controller() {
25 Worker *worker = new Worker;
26 worker->moveToThread(&workerThread);
27 connect(workerThread, &QThread::finished, worker, &QObject::deleteLater);
28 connect(this, &Controller::operate, worker, &Worker::doWork);
29 connect(this, &Controller::kill, worker, &Worker::stopWork);
30 connect(worker, &Worker::resultReady, this, &Controller::handleResults);
31 workerThread.start();
32 }
33 ~Controller() {
34 workerThread.quit();
35 workerThread.wait();
36 }
37
38 public slots:
39 void handleResults(const QString &);
40
41 signals:
42 void operate(const QString &);
43 void kill();
44 };

使用新方式,子线程中的槽都在子线程中运行,主线程中的槽都在主线程中运行,信号和槽默认使用自动连接(AutoConnection)。值得注意的是,如果槽doWork中有耗时操作,比如说while循环,主线程的信号kill子线程是不会响应的,除非使用直接连接(DirectConnection),connect(this, &Controller::kill, worker, &Worker::stopWork, Qt::DirectConnection);,此时,槽stopWork工作于主线程。
3.GUI界面假死的处理
在GUI程序中,主线程也叫GUI线程,因为它是唯一被允许执行GUI相关操作的线程。对于一些耗时的操作,如果放在主线程中,就是出现界面无法响应的问题。这种问题的解决一种方式是,把这些耗时操作放到次线程中,还有一种比较简单的方法:在处理耗时操作中频繁调用QApplication::processEvents()。这个函数告诉Qt去处理那些还没有被处理的各类事件,然后再把控制权返还给调用者。
Qt多线程和GUI界面假死(run()是线程的入口,就像main()对于应用程序的作用。分析QThread::exec函数的源码,旧的QMutexLocker模式其实很好用,挡住别人进入抢占资源,可照抄)good的更多相关文章
- WinForm多线程+委托防止界面假死
当有大量数据需要计算.显示在界面或者调用sleep函数时,容易导致界面卡死,可以采用多线程加委托的方法解决 using System; using System.Collections.Generic ...
- WinForm程序界面假死,寻求完美解决方案
故事的开端是这样的,小白是一个程序员,他确实也是一个小白,目前还在程序员发展的道路上,兢兢业业的小心求学. 有一天,小白接到一个任务,完成一个Winform程序,附加一个功能就是可以读IC卡. 小白终 ...
- C# Winform 窗体界面”假死”后台线程阻塞 解决办法–BeginInvoke
原文:C# Winform 窗体界面"假死"后台线程阻塞 解决办法–BeginInvoke 这个方法可以用在任何后台任务耗时较长,造成界面“假死”界面控件不更新的情况. 比如要要执 ...
- WinForm查询大数据界面假死,使用异步调用解决
用DataGridView无分页绑定一个几千条数据的查询,查询的时候界面直接卡死十几秒,用户体验非常不好,因此用异步操作解决界面卡死的问题原本场景:点击[查询]后,界面直接卡死优化场景:点击[查询]后 ...
- C#中异步及winform中界面假死
c#中可以用BeginInvoke去启动异步调用,但是有两个BeginInvoke一个是controller的BeginInvoke还有一个是委托的BeginInvoke. 主要区别是controll ...
- 解决c# progressBar更新出现界面假死
最近一个项目需求中的一个功能是需要用progressBar反映处理文件的进度. 研究了Invoke和BeginInvoke方法. Control.Invoke 方法 (Delegate) :在拥有此控 ...
- 使用publisher模式控制频繁的UI输出,避免Winform界面假死
http://www.cnblogs.com/Charltsing/p/publisher.html 最近测试task并发任务的效率与线程池的区别,发现了另外一个问题.task建立任务的速度很快,输出 ...
- qt开发ROS gui界面环境配置过程总结
这段时间花了点时间配置了在qtcreator5.9.1上开发ros gui界面的环境,终于可以实现导入工程,插断点调试了.总结起来需要注意以下几点: 1.安装插件ros_qtc_plugin,ROS与 ...
- WPF界面假死
首先要检查那些滥用 Timer.Dispacher Timer 或者滥用什么“线程+死循环+阻塞”轮询的代码. 这种是编程大忌,有些人不会设计事件驱动程序,而是滥用轮询. 若是:触发事件后的假死,搜W ...
随机推荐
- 【CS Round #43 C】Rectangle Partition
[链接]点击打开链接 [题意] 有一辆火车,它的长度为L,然后假设这辆车现在随机可能地出现在0..D之间,然后假设它已经耗光了油. 问你它需要走的期望距离是多少. 这里要走的距离指的是车里最近的加油站 ...
- C#中防止程序多次运行
C#中如何防止程序多次运行?只要在程序入口点函数Main()中的开始部分添加如注释部分的代码,就能快捷实现. 示例代码如下: using System; using System.Collecti ...
- WIN7/8/10下批处理提权工具
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 自动添加批处 ...
- JS学习笔记 - 面向对象 - 选项卡 (普通选项卡改写)
选项卡3 <script> window.onload=function () { new TabSwitch('div1'); }; function TabSwitch(id) // ...
- Android原生生成JSON与解析JSON
JSON数据是一种轻量级的数据交换格式,在Android中通常应用于client与server交互之间的传输数据.像如今在网上有非常多解析JSON数据的jar包,可是归根究竟用的都是Android原生 ...
- 【Codeforces Round #437 (Div. 2) A】Between the Offices
[链接]h在这里写链接 [题意] 在这里写题意 [题解] 在这里写题解 [错的次数] 0 [反思] 在这了写反思 [代码] #include <bits/stdc++.h> using n ...
- shrio 权限管理filterChainDefinitions过滤器配置(转)
/** * Shiro-1.2.2内置的FilterChain * @see ============================================================= ...
- js进阶 12-7 如何知道你是从哪个元素移动到当前元素与事件调用时如何添加额外数据
js进阶 12-7 如何知道你是从哪个元素移动到当前元素与事件调用时如何添加额外数据 一.总结 一句话总结:event的relatedTarget属性和data属性. 1.如何知道你是从哪个元素移动到 ...
- Textillate.js有什么用及使用实例
Textillate.js有什么用及使用实例 一.总结 一句话总结:Textillate.js – 实现动感的 CSS3 文本动画的简单插件 二.textillate.js 文字动画 textilla ...
- 6、USB Video Class Specification
关于USB Class 将设备归为既定的类别,并对相应类别的设备的在USB协议的应用级协议和接口作出规范,这样只要按照类设备的标准实现驱动程序和设备,则一套驱动可以驱动这一类的所有设备,而这一类设备可 ...