Qt 平臺(tái)中使GUI保持響應(yīng)流暢
如何使Qt 平臺(tái)中的GUI保持響應(yīng)流暢?一般來說耗時(shí)較長(zhǎng)的操作,分為計(jì)算密集型操作和IO密集型操作,對(duì)于這兩類操作如何提高響應(yīng)速度。
而從操作的本質(zhì)上來說,操作又可分為不可分解操作,如在第三方庫(kù)中耗時(shí)較長(zhǎng)的操作,以及可分解操作,其中可分解操作又可細(xì)分為串行操作和可并行操作,如何針對(duì)這幾類操作來提高響應(yīng)速度呢?
如何將異步的操作,如網(wǎng)絡(luò)庫(kù)中異步的讀取數(shù)據(jù)方法,變成同步的操作?
此外對(duì)于多線程,普遍的看法是可以提供程序的運(yùn)行速度,其實(shí)不然,不正確地使用線程常常會(huì)使程序變慢,那么在QT中是否可以通過多線程以外的方法來提高響應(yīng)速度呢?本文給你解決。
首先什么是對(duì)GUI的響應(yīng)?答:GUI的響應(yīng)就是系統(tǒng)對(duì)于GUI事件的處理速度。
由于系統(tǒng)處理事件需要一定的時(shí)間,所以一般窗口系統(tǒng)都會(huì)提供一個(gè)事件隊(duì)列來存儲(chǔ)事件。如果把每個(gè)事件處理看成一個(gè)任務(wù)的話,那么事件處理就類似于操作系統(tǒng)對(duì)于任務(wù)按照優(yōu)先級(jí)進(jìn)行處理,使得每個(gè)任務(wù)的平均等待時(shí)間最小。那么就可以借鑒一下操作系統(tǒng)中的方法,比如:
分級(jí),讓較長(zhǎng)的任務(wù)延后執(zhí)行。
分時(shí),對(duì)于較長(zhǎng)的任務(wù),讓其執(zhí)行一段時(shí)間后暫停,然后再執(zhí)行。
減少每個(gè)任務(wù)運(yùn)行的耗時(shí),當(dāng)然這是最基本的方法。
先看***個(gè)分級(jí),當(dāng)一個(gè)事件處理程序知道自己將執(zhí)行耗時(shí)很長(zhǎng)的操作時(shí),可以調(diào)用QCoreApplication::processEvents() 方法,等待消息隊(duì)列中的方法都執(zhí)行完再執(zhí)行。當(dāng)然這是一個(gè)最基本的方法,只適用與簡(jiǎn)單的情況,如果事件隊(duì)列中的另一程序也調(diào)用了該方法則會(huì)出現(xiàn)死鎖。
再看分時(shí),它適用于可分解的操作(包括串行操作和并行操作),只需記錄當(dāng)前任務(wù)的執(zhí)行情況,然后可以再次執(zhí)行。它的使用流程如下:
- Function EventHandler()
- {
- //開始計(jì)時(shí)
- While(執(zhí)行時(shí)間 < 用戶可接受的響應(yīng)時(shí)間)
- {
- //執(zhí)行操作: }
- //注冊(cè)系統(tǒng)空閑事件以繼續(xù)處理
- }
在QT中注冊(cè)系統(tǒng)空閑事件的方法可以通過QTimer::singleShot(0, this, SLOT(calculate()));將系統(tǒng)空閑信號(hào)注冊(cè)到自己的槽中?;蛘呤褂肣MetaObject::invokeMethod(this, "calculate", Qt::QueuedConnection);方法,通過invokeMethod異步的執(zhí)行某個(gè)方法。
***重點(diǎn)看一下如何減少響應(yīng)的時(shí)間,對(duì)于數(shù)據(jù)密集型操作,推薦使用ThreadPool來管理,減少線程上下文切換的時(shí)間;而對(duì)于IO密集型操作,則自己管理一個(gè)thread來實(shí)現(xiàn),而這也是我認(rèn)為thread最應(yīng)該使用的情景,即讓CPU和外設(shè)都處于滿負(fù)荷運(yùn)轉(zhuǎn)狀態(tài),減少總的操作時(shí)間。
對(duì)于并行操作響應(yīng)時(shí)間的減少,在QT中引入了Qt Concurrent的概念,采用Map/Reduce的方式,具體可以參考QT中的Concurrent Programming節(jié)。
***再解答下如何在QT中將異步操作改成同步操作的方法,這個(gè)就屬于QT special的內(nèi)容,一般的讀者可以跳過。
具體的代碼如下所示:
- 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類,它將創(chuàng)建一個(gè)本地的Event loop,然后block,直到接受到finished信號(hào),或者timeout超時(shí)信號(hào)后才退出,而事件循環(huán)則不會(huì)被block。
【編輯推薦】