如何保持GUI的响应流畅(QT平台)

引子 网络

  通常来讲耗时较长的操做,分为计算密集型操做和IO密集型操做,对于这两类操做如何提升响应速度呢? 多线程

  而从操做的本质上来讲,操做又可分为不可分解操做,如在第三方库中耗时较长的操做,以及可分解操做,其中可分解操做又可细分为串行操做和可并行操做,如何针对这几类操做来提升响应速度呢? 异步

  如何将异步的操做,如网络库中异步的读取数据方法,变成同步的操做? oop

  此外对于多线程,广泛的见解是能够提供程序的运行速度,其实否则,不正确地使用线程经常会使程序变慢,那么在QT中是否能够经过多线程之外的方法来提升响应速度呢? ui

  下文将一一解答这些问题。 this

  正文 操作系统

  首先什么是对GUI的响应?答:GUI的响应就是系统对于GUI事件的处理速度。 线程

  因为系统处理事件须要必定的时间,因此通常窗口系统都会提供一个事件队列来存储事件。若是把每一个事件处理当作一个任务的话,那么事件处理就相似于操做系统对于任务按照优先级进行处理,使得每一个任务的平均等待时间最小。那么就能够借鉴一下操做系统中的方法,好比: code

  分级,让较长的任务延后执行。 队列

  分时,对于较长的任务,让其执行一段时间后暂停,而后再执行。

  减小每一个任务运行的耗时,固然这是最基本的方法。

  先看第一个分级,当一个事件处理程序知道本身将执行耗时很长的操做时,能够调用QCoreApplication::processEvents() 方法,等待消息队列中的方法都执行完再执行。固然这是一个最基本的方法,只适用与简单的状况,若是事件队列中的另外一程序也调用了该方法则会出现死锁。

  再看分时,它适用于可分解的操做(包括串行操做和并行操做),只需记录当前任务的执行状况,而后能够再次执行。它的使用流程以下:

function EventHandler(){
     //开始计时
While(执行时间 < 用户可接受的响应时间)
{
         //执行操做
}
    //注册系统空闲事件以继续处理
}

  在QT中注册系统空闲事件的方法能够经过QTimer::singleShot(0, this, SLOT(calculate()));将系统空闲信号注册到本身的槽中。或者使用QMetaObject::invokeMethod(this, "calculate", Qt::QueuedConnection);方法,经过invokeMethod异步的执行某个方法。

  最后重点看一下如何减小响应的时间,对于数据密集型操做,推荐使用ThreadPool来管理,减小线程上下文切换的时间;而对于IO密集型操做,则本身管理一个thread来实现,而这也是我认为thread最应该使用的情景,即让CPU和外设都处于满负荷运转状态,减小总的操做时间。

  对于并行操做响应时间的减小,在QT中引入了Qt Concurrent的概念,采用Map/Reduce的方式,具体能够参考QT中的Concurrent Programming节。

  最后再解答下如何在QT中将异步操做改为同步操做的方法,这个就属于QT special的内容,通常的读者能够跳过。

  具体的代码以下所示:

QNetworkAccessManager manager;
QEventLoop q;
QTimer tT;
tT.setSingleShot(true);
connect(&tT, SIGNAL(timeout()), &q, SLOT(quit()));
connect(&manager, SIGNAL(finished(QNetworkReply*)), &q, SLOT(quit()));
QNetworkReply *reply = manager.get(QNetworkRequest(QUrl("http://www.qtcentre.org")));
tT.start(5000); // 5s timeout

q.exec();
if(tT.isActive()){
    // download complete
 tT.stop();
} else {
  // timeout
}

  其中主要利用了QEventLoop类,它将建立一个本地的Event loop,而后block,直到接受到finished信号,或者timeout超时信号后才退出,而事件循环则不会被block。

  总结

  本文分析了影响GUI响应速度的缘由,经过类比操做系统对任务的优先级分派方式,探讨了对多种事件类型,提升其响应速度的方法,其中有些方法在其它平台也是通用的。

相关文章
相关标签/搜索