1. 程式人生 > >qt延時之介面“假死”問題

qt延時之介面“假死”問題

轉載自:https://blog.csdn.net/xiezhongyuan07/article/details/81169583

在Qt程式中,我們有時候會遇到這樣的需求,比如讓程式暫停(休息、休眠)一段時間, 這段時間裡可能程式什麼都不做, 也可能是在後臺開了個子執行緒在做一些使用者看不見的工作。

最開始我需要這樣的需求時候,我第一反應想到的是在QT Assistant 中搜索sleep方法,企圖尋找讓程式暫停、休眠一段時間的方法,不過,搜尋結果顯然令我很失望,並沒有找到直接滿足需求的sleep方法,不過看到了QThread中的sleep的方法:

[static] void QThread::sleep(unsigned long secs);//Forces the current thread to sleep for secs seconds.

這個好像和我們的需求類似,就試一下吧。

QThread::sleep(5000);

好吧,結果卻是下面這樣,也就是程式直接崩潰了。

究其原因:

  • 原來介面的執行緒是主執行緒,在主執行緒中使用休眠函式是一種錯誤,這會直接導致介面無法重新整理,使用者與程式無法互動,最終導致程式崩潰。linux提供的”sleep”或”usleep”函式會將你當前的執行緒/程序變為“睡眠”狀態。 這個“睡眠”是深度意義的睡眠, 睡眠期間核心不會分配給程式時間片, 所以程式什麼都不做, 更不用提介面的重新整理了。 直接導致的問題就是使用者無法與程式互動。 所以說直接使用sleep函式睡眠是常見的錯誤方案之一。
  • 多執行緒程式使用sleep()、msleep()、usleep()、wait()進行延時處理。Sleep不會釋放物件鎖,其他執行緒無法訪問物件,因此會阻塞執行緒;而Wait會釋放物件鎖,使得其他執行緒能夠訪問該物件。

我們要記住最重要的一點:我們永遠不需要讓主執行緒休眠,但是,我們有時候會需要讓程式等一段時間。

那麼如何讓程式等待一段時間呢? 對,有的小夥伴可能已經想到了使用定時器或者死迴圈等方式,如下:


QTime time;

time.start();

while(time.elapsed() < 5000); //等待時間流逝5秒鐘

這樣做會存在一個問題,當在死迴圈的時候,我們的介面是無法重新整理,使用者是不會響應使用者的任何互動的。也就是讓使用者感覺程式已經是假死狀態了。 從程式碼中我們可以發現在while迴圈中不停的呼叫elapsed()函式, 等於在這段時間內CPU完全沒有機會做別的什麼事情。 特別是在Linux這樣非搶佔式的作業系統中, 這樣的死迴圈造成的影響是致命的, CPU被完全佔用, 核心都沒有機會排程程序, 別的程式拿不到時間片執行, 系統基本上就是癱瘓狀態了。 無論如何, 這種結果都不是我們想要的。(當然拉, 除非你想寫的是病毒程式。) 對於我們的程式本身, 雖然它佔用了所有的CPU, 但由於它陷入該迴圈, 程式沒有機會進入到GUI事件迴圈, 導致同樣介面是無法重新整理的。

那麼怎樣改成正確的呢? 只需要在死迴圈的時候,不停地處理事件,讓程式保持響應。


QTime time;

time.start();

while(time.elapsed() < 5000)             //等待時間流逝5秒鐘
{
    QCoreApplication::processEvents();   //處理事件
    //usleep(10000);//sleep和usleep都已經obsolete,建議使用nanosleep代替
}

    

為解決CPU佔用率過高的問題,可以讓程式適當的睡眠。這裡設定程式睡眠一段很短很短的時間, 對於使用者來說是不會有什麼感覺的, 但對核心來說就意義大不同。 這樣核心就有充足的時間排程程序/執行緒, 讓其他程式有機會執行。

其實還有另外一種方式實現類似的效果,就是定時器與QEventLoop配合使用:


    QEventLoop eventloop;

    QTimer::singleShot(5000, &eventloop, SLOT(quit()));

    eventloop.exec();