正確使用Qt多線程

QThread的常見特性:html

run()是線程的入口,就像main()對於應用程序的做用。QThread中對run()的默認實現調用了exec(),從而建立一個QEventLoop對象,由其處理該線程事件隊列(每個線程都有一個屬於本身的事件隊列)中的事件。簡單用代碼描述以下:安全

int QThread::exec()
{
//...
    QEventLoop eventLoop;
    int returnCode = eventLoop.exec();
//...
    return returnCode;
}
 
int QEventLoop::exec(ProcessEventsFlags flags)
{
//...
    while (!d->exit) {
        while (!posted_event_queue_is_empty) {
            process_next_posted_event();
        }
    }
//...
}

因而可知,exec()在其內部不斷作着循環遍歷事件隊列的工做,調用QThreadquit()exit()方法使中止工做,儘可能不要使用terminate(),該方法過於粗暴,形成資源不能釋放,甚至互斥鎖還處於加鎖狀態。函數

1. 舊的使用方式:oop

#include "QThread"
#include "QMutexLocker"
#include "QMutex"
class Thread:public QThread
{
    Q_OBJECT
public:
    Thread();
    void stop();
private:
    bool m_stopFlag;
    QMutex mutex;
protected:
    void run();
};
Thread::Thread()
{
    m_stopFlag = false;
}
 
void Thread::stop()
{
    QMutexLocker locker(&mutex);
    m_stopFlag = true;
}
 
void Thread::run()
{
    while(1){
        {
            QMutexLocker locker(&mutex);
            if(m_stopFlag)
                break;
        }
        qDebug()<<"This is in thread["<<currentThreadId()<<"]."<<(int)currentThread();
        sleep(2);
    }
    m_stopFlag = false;
}

這是qt4.6及以前的使用方法,這種方式本沒有什麼錯誤,能夠處理咱們的絕大多數需求。下面的調用能夠看出Thread對象自己工做在主線程下,即便調用的t.stop()方法,它也是工做在主線程下,只有run()範圍內的代碼工做在次線程中。post

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug()<<"From main thread: "<<QThread::currentThreadId();
 
    Thread t;
    QObject::connect(&t, SIGNAL(finished()), &a, SLOT(quit()));
    t.start();
    return a.exec();
}

2. 推薦的使用方式:ui

#include <QtCore>
class Worker : public QObject
{
    Q_OBJECT
private slots:
    void onTimeout()
    {
        qDebug()<<"Worker::onTimeout get called from?: "<<QThread::currentThreadId();
    }
};
 
#include "main.moc"
 
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug()<<"From main thread: "<<QThread::currentThreadId();
 
    QThread t;
    QTimer timer;
    Worker worker;
 
    QObject::connect(&timer, SIGNAL(timeout()), &worker, SLOT(onTimeout()));
    timer.start(1000);
 
    worker.moveToThread(&t);
 
    t.start();
 
    return a.exec();
}

這是Qt4.7及之後版本推薦的工做方式。其主要特色就是利用Qt的事件驅動特性,將須要在次線程中處理的業務放在獨立的模塊(類)中,由主線程建立完該對象後,將其移交給指定的線程,且能夠將多個相似的對象移交給同一個線程。在這個例子中,信號由主線程的QTimer對象發出,以後Qt會將關聯的事件放到worker所屬線程的事件隊列。因爲隊列鏈接的做用,在不一樣線程間鏈接信號和槽是很安全的。spa

    說說connect最後一個參數,鏈接類型:線程

    1)自動鏈接(AutoConnection),默認的鏈接方式,若是信號與槽,也就是發送者與接受者在同一線程,等同於直接鏈接;若是發送者與接受者處在不一樣線程,等同於隊列鏈接。code

    2)直接鏈接(DirectConnection),當信號發射時,槽函數當即直接調用。不管槽函數所屬對象在哪一個線程,槽函數總在發送者所在線程執行。htm

    3)隊列鏈接(QueuedConnection),當控制權回到接受者所在線程的事件循環時,槽函數被調用。槽函數在接受者所在線程執行。

3.GUI界面假死的處理

    在GUI程序中,主線程也叫GUI線程,由於它是惟一被容許執行GUI相關操做的線程。對於一些耗時的操做,若是放在主線程中,就是出現界面沒法響應的問題。這種問題的解決一種方式是,把這些耗時操做放到次線程中,還有一種比較簡單的方法:在處理耗時操做中頻繁調用QApplication::processEvents()。這個函數告訴Qt去處理那些尚未被處理的各種事件,而後再把控制權返還給調用者。

QElapsedTimer et;
et.start();
while(et.elapsed()<300)
    QCoreApplication::processEvents();

http://blog.debao.me/2013/08/how-to-use-qthread-in-the-right-way-part-1/

http://qt-project.org/doc/qt-4.8/qthread.html

http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/

相關文章
相關標籤/搜索