Qt線程實現分析-moveToThread vs 繼承


最近抽空研究了下QThread,使用起來方式多種多樣,但是在使用的同時,我們也應該去了解Qt的線程它到底是怎么玩兒的。

Qt的幫助文檔里講述了2種QThread的使用方式,一種是moveToThread,另一種是繼承QThread實現run方法,下面我們分別來分析下

一、moveToThread

首先我們來先分析move這種方式,他的使用可能像下面這樣

class Worker : public QObject
{
public slots:
    void doWork(const QString &) {
        emit resultReady(result);
    }
};

class Controller : public QObject
{
    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();
    }
};

這是一個標准的多線程使用方式,復雜的邏輯操作我們可以放在Worker對象的槽函數中進行,因為只有槽函數是在工作線程中執行的,下面我記錄了各個函數執行時所在的線程ID

由於線程ID是每次會發生編號,可能每個人測試的結果不一樣

  • Worker(): 0x4c34 主線程
  • doWork(): 0x40c8 工作線程
  • handleResults(): 0x4c34 主線程
  • ~Worker(): 0x40c8 工作線程

細心的同學就會發現了,Worker對象的構造函數和析構函數不在同一個線程里邊:Worker對象的事件循環已經放到子線程中了,Worker對象刪除時,是工作線程通過拋出DeferredDelete事件執行的

下面結合我自己之前的一些使用理解,來分析下moveToThread是如何運作的:

假設有這么一種場景,需要把對象obj從線程A移動到線程B

首先我自己看了Qt的這個函數源碼,這里把他翻譯成為了白話文,我們大家可以來看下

1、一些異常判斷

  1. 確認不在同一個線程里
  2. 移動的對象不能有父類
  3. 不能移動Widget窗體
  4. 支持移動一個無所屬線程的對象到指定線程
  5. 對象不在C線程時,C線程不能把對象移動到B線程,只有A線程可以

2、moveToThread_helper

  1. 構造ThreadChange事件,發送給自己
  2. 迭代所有子對象,並執行moveToThread_helper方法

3、setThreadData_helper

  1. 循環遍歷,把線程A中obj對象的所有事件移動到B線程中
  2. 如果移動了新事件到線程B中,則我們需要喚醒B線程,讓他去派發事件
  3. 迭代所有子對象,並執行setThreadData_helper方法

二、繼承QThread

假設說我們繼承QThread實現了一個UsThread,使用起來可能像這樣

UsThread thd;

經過我的實踐,很可惜,除了run函數以外,所有的函數執行,包括對象都在主線程中

如果你想着thd.moveToThread這么干,那么可能會被打死

結論:個人推薦使用moveToThread這種方式進行子線程編寫

更詳細的測試結果可以參考
QThread使用——關於run和movetoThread的區別


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM