1. 程式人生 > >Qt延時/等待寫法

Qt延時/等待寫法

1 阻塞型延時

阻塞的原理就是:在延時期間,本執行緒的事件迴圈得不到執行。
(1)QThread類的sleep()
最簡單的延時方法就是使用QThread類的sleep(n)、msleep(n)、usleep(n),這幾個函式的不良後果就是,GUI會在延時的時間段內失去響應,介面卡死,所以,這三個函式一般用在非GUI執行緒中。

QThread::msleep(50);//阻塞延時50ms

(2)使用定時器:死等

void Delay_MSec_Suspend(unsigned int msec)
{    
    QTime _Timer = QTime::currentTime().addMSecs
(msec); while( QTime::currentTime() < _Timer ); }

2 非阻塞延時

原理無非就是利用事件迴圈,有兩種原理:
(1)處理本執行緒的事件迴圈
在等待中,不斷強制進入當前執行緒的事件迴圈,這樣可以把堵塞的事件都處理掉,從而避免程式卡死。

void Delay_MSec(unsigned int msec)
{
    QTime _Timer = QTime::currentTime().addMSecs(msec);
    while( QTime::currentTime() < _Timer )
        QCoreApplication::processEvents
(QEventLoop::AllEvents, 100);

QCoreApplication::processEvents(QEventLoop::AllEvents, 100);//這條語句能夠使程式在while等待期間,去處理一下本執行緒的事件迴圈,處理事件迴圈最多100ms必須返回本語句,如果提前處理完畢,則立即返回這條語句。這也就導致了該Delay_MSec函式的定時誤差可能高達100ms。
(2)使用子事件迴圈

建立子事件迴圈,在子事件迴圈中,父事件迴圈仍然是可以執行的

void Delay_MSec(unsigned int msec)
{
    QEventLoop loop;//定義一個新的事件迴圈
QTimer::singleShot(msec, &loop, SLOT(quit()));//建立單次定時器,槽函式為事件迴圈的退出函式 loop.exec();//事件迴圈開始執行,程式會卡在這裡,直到定時時間到,本迴圈被退出 }

3 耗時程式碼的處理

假設有這樣的應用情景:點選某個button之後,需要讀入並處理一幅影象,需要耗時20秒才能處理完。

在這20s內,GUI會失去效應,介面上的任何元素都無法被點選,這種情況應該怎麼辦?方法有兩種:1、用另一個執行緒去處理這個耗時任務;2、在耗時任務中,不斷地去處理本執行緒的事件迴圈,以保證GUI的及時響應。

for(i=0; i < 1000000; i++)
{
    //QCoreApplication::processEvents(QEventLoop::AllEvents);    //去處理本執行緒的事件迴圈,避免本執行緒被堵塞
    QCoreApplication::processEvents(QEventLoop::AllEvents, 5);//如果不夠頻繁,可以增加第二引數來緩解卡頓

    for(j=0; j < 1000000; j++)
    {
        //QCoreApplication::processEvents(QEventLoop::AllEvents);//處理事件迴圈,不建議放在這裡,可能過於頻繁
        doSomeThing();
    }
}

一般來說,processEvents()不宜被呼叫的過於頻繁,也不宜被呼叫的不夠頻繁。過於頻繁的話,一方面會使執行緒的響應更好,但另一方面會導致原本就耗時的任務變得更加耗時;不夠頻繁的話,顯然可能會使GUI執行緒的響應變差,例如每500ms才被呼叫一次,那麼GUI的事件迴圈就只能500ms才被處理一次,當然,這個問題可以通過設定processEvents()的第二個形參略微得到緩解,更好的做法是,保證被調的週期<200ms(再小一些更好,看程式需求),這樣不至於肉眼可見的卡頓。

副作用:(特別注意!)

1、在點選按鈕之後,這個20s的耗時任務開始執行,尚未執行完畢時,我們點選了GUI的關閉按鈕,那麼GUI會立即消失,但是這個耗時任務仍然會在後臺執行,直到執行完畢,程序才會退出。解決辦法:重寫關閉事件,在關閉事件的函式中直接結束程序。

2、在點選按鈕之後,這個20s的耗時任務開始執行,執行到第5秒時,我們再次點選了這個按鈕,那麼QT又會執行一個新的20s任務,這個新任務完成後,又會接著把第一個20s任務從上次被打斷的第5秒繼續執行。如果這個任務是可重入的,後果僅僅是被執行了兩遍,如果任務不可重入,那情況就徹底糟糕了。解決辦法:點選按鈕後把這個按鈕disable掉,執行完再enable