最开始使用Qt时就遇到过QT Gui失去响应的问题,我是用多线程的方式解决的,然而通常来说,多线程是会降低程序的运行速度。

之后,在使用QSqlQuery::execBatch()函数时,Qt Gui 又失去响应,虽然多线程可以解决,但是如果能用单线程很好解决的,最好不要用到多线程,因为多线程不仅容易拖慢程序的速度,编程及维护的难度也更大,能用简单方法解决的,就不要用复杂的方法。

于是我再次搜索资料,期望在解决方案的选择与解决步骤上,能够得到一个全面而又细致的总结。


Witold Wysota 的文章https://doc.qt.io/archives/qq/qq27-responsive-guis.html#performinglongoperations 总结的非常不错。

Jason Lee的翻译: http://blog.csdn.net/jasonblog/article/details/5568589

所以本文是在此文基础上的部分翻译、理解与二次总结。总之,有删减,有补充,所以没写 '转' 字。


一、问题的来源与分析

首先,我们要知道 “为什么Qt Gui 会停止响应?”。简明扼要的说就是:长时间的密集处理或等待阻塞了Qt的事件循环,应用程序不能响应来自窗口系统的事件请求(《C++ Gui Qt4》 P135中有描述)。   那么多长算长呢?一秒钟算长,两秒钟太长。

其次,“ 何种情形下会发生该问题? ”。可分为两种情形:

第一,长时间按顺序执行的密集运算,全部计算结束后才能继续执行,如快速傅立叶变换。

第二,“ 触发 ”了某项操作,该操作完成后才能进行“ 下一步 ”, 所以这里描述的是异步操作,如保存文件操作,服务器等待连接、网络下载等。详细见附注(1).

私以为两种情形并无明显的概念上的区分,本质是一样的,但两种情形有不同的处理方法,特别是第二种情形, 在Qt框架下 ,用Qt的信号和槽机制往往可以解决阻塞问题,如QTcpServer::newConnection信号通知连接的到来,QIODevice::bytesWritten()与 QIODevice::readyRead()通知文件的读写,它们都是以非阻塞的形式实现相关功能的利器。 而第一种情形,不仅所有的事件循环停止了,信号和槽也暂时被忽视。我们将针对以上两种情形寻找解决方案。

最后,我们考虑是否可以把这个造成 Qt Gui 停止响应的罪魁祸首大卸八块,即把他拆分成一个个小块,如果可以拆分,那么每块之间是依赖还是独立,如果独立那问题好办,放在不同的位置独立运作,否则,我们只能同步的执行,而最差的结果是——根本无法拆分!!

总之,考虑以上信息差异,执行不同的解决方案。


二、解决方案

Manual event processing(人工执行事件)

保持事件循环有一种最基本的方法——让程序去处理 悬挂事件好了 ,处理完了再回来继续我的后续运算,要做到这一点,就要在我的运算代码中间加上处理事件的代码,这句代码就是 QCoreApplication::processEvents();,只要该句代码能够周期性的被执行,就能保持Qt Gui的响应。

//代码来源于上述链接所指向文章

for (int i = ; i <= sqrt(x) && isPrime; i += ) {
label->setText(tr("Checking %1...").arg(i));
if (x % i == )
isPrime = false;
QCoreApplication::processEvents();
if (!pushButton->isChecked()) {
label->setText(tr("Aborted"));
return;
} }

部分翻译(略)——可查看 Jason Lee的翻译

该方案除了 具有Witold Wysota文中 所提到缺点之外,《C++ Gui Qt4》P135中还提到,用户可能会在应用程序还在执行某种操作时,或者关闭了主窗口,或者通过界面再次触发相同操作,这样就会产生不可预料的后果,如 一个保存文件对话框,用户单击save按钮后,程序开始磁盘文件的写入操作,该操作还未完成时,用户再次单击了关闭按钮,或者再次单击save按钮。书中给出的解决办法是将 qApp->processEvents()替换为qApp-> processEvents(QEventLoop::ExcludeUserInputEvents),以告诉Qt忽略鼠标事件和键盘事件。

Using a Worker Thread(使用任务线程)

翻译(略)—— Jason Lee的翻译

除了 Witold Wysota 文中所说的重新实现QThread类之外,还可以使用QObject::moveToThread(QThread *thread)函数,将

进行复杂运算的对象移入子线程中运行,前提是子线程不能够有父对象,否则无法移入子线程。示例如下:

QThread *thread = new QThread(this);
MyComputation *computation = new MyComputation();//负责密集运算的对象
computation->moveToThread(thread);
connect(thread, SIGNAL(started()), computation ,SLOT( compute()) ); //compute()为computation的运算函数
thread->start();

需注意的是,将 computation 对象移入子线程后, 依旧不可直接调用  computation 对象 compute()函,应该调用线程对象的start()函数,发出started()信号触发 computation 对象的运算操作,否则依旧会阻塞主线程。

Waiting in a Local Event Loop(在本地事件循环中等待)

翻译(略)—— Jason Lee的翻译

注解:如文章开头所说,“等待异步事件完成”,也就是说这种方法是针对异步事件而设计的,异步事件执行过程中会不断发送信号,我们根据该信号决定程序接下来的行为,包括人工执行事件。而  Manual event processing 适用于顺序执行的操作 。

Solving a Problem Step by Step(分步骤解决问题)

翻译(略)

如前文所说,如果一个复杂操作可以拆分为独立的子操作,那么拆分应该是最好的解决办法。至于如何拆分,可以通过阅读《重构》这本书来学习。

Parallel Programming
翻译(略)


三、总结

前面我提到过,我是用queryBatch()函数导致了Qt Gui无法响应的,最后我选择了 Using a Worker Thread这种方法。queryBatch()是一个操作数据库的批处理函数,非常便利,但它是顺序执行的,我无法用异步方式来处理它,函数内部是不可见的,也无法人工执行事件或者拆分,最后只能使用子线程来执行它了,这就是为便利所付出的代价吧。不同情况有不同的解决方案,认清自己的问题很重要。


四、附注

(1) “ 触发 ”了某项操作,该操作完成后才能进行“ 下一步 ”,但实际上对于“触发”这个行为本身而言,它的职责已经完成了,而

“下一步”指的是某个功能执行中的“下一步”,当然也可能是编程语境下的“ 下一步 ”,即下一条语句, 所以这里描述的是异步操作,如保存文件操作,服务器等待连接、网络下载等。

举个栗子, 创建了QTcpServer对象并调用listen() 函数监听连接,这时如果调用QTcpServer::waitForNewConnection(...)函数,就会阻塞程序的运行,直到连接到来函数才能返回,进而执行下一句。这里,listen()函数“ 触发 ”了监听行为, “ 下一步 ”与网络连接相关的动作要等连接到来后才能执行,而 QTcpServer::waitForNewConnection(...)则作为我们是否执行下一步的判断标准,只不过这里用的是阻塞的方式;对于异步操作,也可以用非阻塞的方式解决,Qt的信号和槽机制就能很好的解决,如我们可以接收newConnection()信号判断连接是否到来,而不必将程序阻塞在那。

保持Qt GUI响应的几种方法的更多相关文章

  1. Qt中显示图像的两种方法

    博客转载自:https://blog.csdn.net/lg1259156776/article/details/52325361 在Qt中处理图片一般都要用到QImage类,但是QImage的对象不 ...

  2. 解决MySQL安装到最后一步未响应的三种方法

    这种情况一般是你以前安装过MySQL数据库服务项被占用了.解决方法: 方法一:安装MySQL的时候在这一步时它默认的服务名是“MySQL” 只需要把这个名字改了就可以了.可以把默认的服务器的名称手动改 ...

  3. Qt 自定义事件(三种方法:继承QEvent,然后Send Post就都可以了,也可以覆盖customEvent函数,也可覆盖event()函数)

    Qt 自定义事件很简单,同其它类库的使用很相似,都是要继承一个类进行扩展.在 Qt 中,你需要继承的类是 QEvent. 继承QEvent类,你需要提供一个QEvent::Type类型的参数,作为自定 ...

  4. Qt 设置背景图片3种方法(三种方法:QPalette调色板,paintEvent,QSS)

    方法1. setStylSheet{"QDialog{background-image:url()"}}  //使用styleSheet 这种方法的好处是继承它的dialog都会自 ...

  5. Jmeter查看结果树之查看响应的13种方法[详解]

    查看结果树查看响应有哪几种方法,可通过左侧面板底部的下拉框选择 1.Text 查看结果树中请求的默认格式为text,会显示请求的取样器结果.请求.响应数据3个部分内容. 取样器结果: 默认Raw视图, ...

  6. GNU Radio下QT GUI Tab Widget的使用方法

    期望显示出的效果: 即将要显示的图放在各自的标签页中. 整体框图: 具体设置: QT GUI Tab Widget的设置: 其中 ID改为自己想改的,这里我写的是display GUI Hint所代表 ...

  7. 【Qt开发】Qt中显示图像的两种方法对比

    在Qt中处理图片一般都要用到QImage类,但是QImage的对象不能够直接显示出来,要想能看到图片,初步发现有两种方法. 一.QImage转QPixmap,然后用QLabel::setPixmap( ...

  8. jmeter 查看结果树之查看响应的13种方法

    查看结果树查看响应有哪几种方法,可通过左侧面板底部的下拉框选择 1 Text 查看结果树中请求的默认格式为Text,显示取样器结果.请求.响应数据3个部分内容. 取样器结果: 默认Raw展示,可以切换 ...

  9. Qt产生随机数(两种方法)

    第一种方法 #include <QTime> QTime time; time= QTime::currentTime(); qsrand(time.msec()+time.second( ...

随机推荐

  1. css3更改input单选和多选的样式

    在项目开发中我们经常会遇到需要更改input单选和多选样式的情况,今天就给大家介绍一种简单改变input单选和多选样式的办法. 在这之前先简单介绍一下:before伪类 :before 选择器向选定的 ...

  2. Android Socket连接PC出错问题及解决

    最近测试问题:Android 通过Socket链接电脑,ip和端口都是正确的,也在同一网段,可android端就是报异常如下: 解决办法:测试电脑的防火墙可能开着,在控制面板把防火墙打开即可.

  3. 【腾讯Bugly干货分享】OCS——史上最疯狂的iOS动态化方案

    本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/zctwM2Wf8c6_sxT_0yZvXg 导语 在 ...

  4. HTML5 摇一摇加强版之一次失败的探索

    最近在看设备传感器的API,当然也少不了研究一下让微信称神的“摇一摇”了.关于“摇一摇”的实现,网上很多资料所以不详细说了,但总是有布局.效果不全等各种问题,所以作为一名资深copypaster,代码 ...

  5. [WinAPI] 获取窗口句柄的几种方法

    1.使用FindWindow函数获取窗口句柄 示例:使用FindWindow函数获取窗口句柄,然后获得窗口大小,并且移动窗口到指定位置. 我们想获得酷我音乐盒的窗口句柄并移动它,该怎么办呢? 首先打开 ...

  6. ASP.NET Web API 管道模型

    ASP.NET Web API 管道模型 前言 ASP.NET Web API是一个独立的框架,也有着自己的一套消息处理管道,不管是在WebHost宿主环境还是在SelfHost宿主环境请求和响应都是 ...

  7. Android,适合Restful网络请求封装

    借助volley.Gson类库. 优点 网络请求集中处理,返回值直接为预期的对象,不需要手动反序列,提高效率,使用时建立好model类即可. 使用效果 DataProess.Request(true, ...

  8. ASP.NET MVC中利用AuthorizeAttribute实现访问身份是否合法以及Cookie过期问题的处理

    话说来到上海已经快半年了,时光如白驹过隙,稍微不注意,时间就溜走了,倒是没有那么忙碌,闲暇之际来博客园还是比较多的,记得上次在逛博问的时候看到有同志在问MVC中Cookie过期后如何作相关处理,他在阐 ...

  9. 【.net深呼吸】动态类型(高级篇)

    前面老周给大家介绍了动态类型使用的娱乐级别用法,其实,在很多情景下,娱乐级别的用法已经满足需求了. 如果,你想自己来控制动态类型的行为和数据的存取,那么,就可以考虑用今天所说的高大上技术了.比如,你希 ...

  10. PHP的学习--使用phar打包

    前段时间写了几个PHP的脚本,但是因为脚本的项目是基于composer安装的,给别人使用的时候不太方便,就希望能够打包成一个能直接使用的文件. 搜索了一下,发现可以使用phar打包. 假设我们有如下一 ...