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 ...
随机推荐
- 日历控件input框默认显示当日日期
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <script sr ...
- Loadrunner--集合点
集合点的意思是等到特定的用户数后再一起执行某个操作,比如一起保存,一起提交(我们通常意义上的并发数并不是指一起提交或者一起保存),一般情况下使用不到集合点,不过,订票系统或者促销类需要用到,比如说某个 ...
- LoadRunner--HTML与URL录制方式区别
Recording录制选项 这里提供了两个大类的录制方式: 1. HTML-based script基于HTML的脚本 这种方式录制出来的脚本是基于HTML基础的,为每个用户操作生成单独的步骤,这种脚 ...
- APP测试10点
1.安装和卸载●应用是否可以在IOS不同系统版本或android不同系统版本上安装(有的系统版本过低,应用不能适配)●软件安装后是否可以正常运行,安装后的文件夹及文件是否可以写到指定的目录里.●安装过 ...
- 【例题 6-17 UVa 10562】Undraw the Trees
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 模拟+递归 [代码] #include <bits/stdc++.h> using namespace std; con ...
- 通达OA 小飞鱼在线开发培训第一讲介绍(图文)
培训课件的主要内容.须要參加培训的同学要注意了.课程内容以有用为主.课件仅供參考.
- iOS_04_学习ios开发的准备
学习ios开发的准备 * 英语水平:看懂26个英文字母. * 计算机专业:不要求计算机专业,但得有脑子. * 学习态度:积极思考.积极动手.能吃苦.有兴趣. * 编程语言:C语言.C++(可选).Ob ...
- 安装GDB和GDBSERVER
安装GDB和GDBSERVER 转自http://www.360doc.com/content/10/0407/17/155970_21971613.shtml 把GDBSERVER装入文件系统 转自 ...
- 9、getopt的用法,被用来解析命令行选项参数
#include <unistd.h> extern char *optarg; //选项的参数指针 extern int optind, //下一次调用ge ...
- client、server端编程
首先是从main函数开发: int main(itn argc,char* argv[]) { pthread_t thread; int count; int status; cli ...