【官方文檔整理】QT多線程(二):QThread、QThreadPool使用示例

QThread使用示例

方法一:繼承自QThread,重寫run()方法

與C++常見的線程實現方法類似,爲實現代碼在單獨線程中運行,可以繼承QThread並重新實現run()。

class WorkerThread : public QThread
{
    Q_OBJECT
    void run() Q_DECL_OVERRIDE {
        QString result;
        /* ... here is the expensive or blocking operation ... */
        emit resultReady(result);
    }
signals:
    void resultReady(const QString &s);
};

void MyObject::startWorkInAThread()
{
    WorkerThread *workerThread = new WorkerThread(this);
    connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
    connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
    workerThread->start();
}

方法二:繼承自QObject,調用QObject::moveToThread()

通過調用QObject :: moveToThread()方法,可以將Worker對象移動到線程來使用。
Worker的插槽中的代碼將在一個單獨的線程中執行。同時,可以自由地將Worker的插槽連接到任何線程中任何對象的任何信號。由於QT採用排隊連接的機制,可以安全地跨線程連接信號和插槽

class Worker : public QObject
{
    Q_OBJECT

public slots:
    void doWork(const QString &parameter) {
        QString result;
        /* ... here is the expensive or blocking operation ... */
        emit resultReady(result);
    }

signals:
    void resultReady(const QString &result);
};

class Controller : public QObject
{
    Q_OBJECT
    QThread workerThread;
public:
    Controller() {
        Worker *worker = new Worker;
        worker->moveToThread(&workerThread);
        connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
        connect(this, &Controller::operate, worker, &Worker::doWork);
        connect(worker, &Worker::resultReady, this, &Controller::handleResults);
        workerThread.start();
    }
    ~Controller() {
        workerThread.quit();
        workerThread.wait();
    }
public slots:
    void handleResults(const QString &);
signals:
    void operate(const QString &);
};

注意事項

  • QThread實例存在於實例化它的舊線程中,而不是在調用run()的新線程中。這意味着所有QThread的排隊插槽都將在舊線程中執行。因此,希望在新線程中調用插槽必須使用worker-object 方法(第二種); 不應將新插槽直接實現到子類QThread中。

  • 在繼承QThread時,請記住構造函數在舊線程中執行,而run()在新線程中執行。如果從兩個函數訪問成員變量,則實際是從兩個不同的線程訪問該變量,需要檢查這樣做是否安全。

QThread管理線程

  • 線程起止信號:當線程開始或結束時,QThread將通過信號started()和finished()通知,或者可以使用isFinished()和isRunning()來查詢線程的狀態。從Qt 4.8開始,通過將finished()信號連接到QObject :: deleteLater(),可以釋放生活在剛剛結束的線程中的對象。
  • 停止線程:可以通過調用exit()或quit()來停止該線程。
  • 阻塞線程:使用wait()來阻塞調用線程,直到另一個線程完成執行(或直到指定的時間過去)。
  • 線程睡眠:QThread還提供靜態的,獨立於平臺的睡眠功能:sleep(),msleep()和usleep()分別允許完整的秒,毫秒和微秒分辨率。注意:一般來說,wait()和sleep()函數是不必要的,因爲Qt是一個事件驅動的框架。而不是wait(),考慮監聽finished()信號。一般考慮使用QTimer,而不是sleep()函數。
  • 線程ID:靜態函數currentThreadId()和currentThread()返回當前正在執行的線程的標識符。前者返回線程的特定於平臺的ID; 後者返回一個QThread指針。

QThreadPool使用示例

QThreadPool管理和重新處理各個QThread對象,以幫助減少使用線程的程序中的線程創建成本。每個Qt應用程序都有一個全局QThreadPool對象,可以通過調用globalInstance()來訪問它。

要使用QThreadPool其中一個線程,可以繼承QRunnable並實現run()虛函數。然後創建該類的對象並將其傳遞給QThreadPool :: start()。

class HelloWorldTaskpublic  QRunnable
{
    void run()
    {
        qDebug()< <  "Hello world from thread "  < <  的QThread :: currentThread();
    }
}

HelloWorldTask * hello =  new HelloWorldTask();
// QThreadPool獲取所有權並自動刪除
hello'QThreadPool :: globalInstance()- > start(hello);

QThreadPool管理線程

  • 自動刪除:QThreadPool默認自動刪除QRunnable。使用QRunnable :: setAutoDelete()更改自動刪除標誌。如果啓用了autoDelete,則當最後一個線程退出運行功能時,將刪除QRunnable。

  • 多次調用:QThreadPool支持通過從QRunnable :: run()中調用tryStart(this)多次執行相同的QRunnable。

  • 過期限制:在一段時間內未使用的線程將過期。默認的到期超時爲30000毫秒(30秒)。可以使用setExpiryTimeout()更改此設置。設置負到期超時會禁用到期機制。

  • 最大線程數:調用maxThreadCount()以查詢要使用的最大線程數。如果需要,您可以使用setMaxThreadCount()更改限制。默認的maxThreadCount()是QThread :: idealThreadCount()。該activeThreadCount()函數返回目前做的線程數工作。

  • 保留和釋放:reserveThread()函數保留外用線程。完成線程後使用releaseThread(),以便可以重用它。實質上,這些函數會臨時增加或減少活動線程數,並且在實現QThreadPool不可見的耗時操作時非常有用。

不過,QThreadPool是用於管理線程的低級類,更高級別的並行API參考Qt Concurrent模塊。

Reference

QThread類
http://doc.qt.io/archives/qt-5.8/qthread.html
QThreadPool類
http://doc.qt.io/archives/qt-5.8/qthreadpool.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章