QThread官方幫助文檔-詳細介紹部分(個人翻譯)

程序中,一個QThread對象能管理控制一個線程。線程啓動於run()。默認下,調用exec()後,run()纔在線程中開啓一個Qt事件循環。
用QObject::moveToThread()將工作對象移動到指定線程。

  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 &);
  };

Worker槽函數代碼將在單獨的線程中執行。你可隨意地將Worker的槽函數連接至任何線程內任何對象的任何信號。得益於queued connections機制,不同線程間的信號槽連接是安全的。

讓代碼運行在獨立線程的另一種辦法是,繼承QThread,重新實現run()。例如:
  class WorkerThread : public QThread
  {
      Q_OBJECT
      void run() 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();
  }

本例中,run函數返回後,單獨線程將退出。除非調用exec(),否則線程內沒有任何事件循環。非常重要的一點,QThread實例存活在初始化他的舊線程中,並不在調用run()的新線程中。也就是說,
QThread的隊列槽和喚醒方法都是在舊線程中執行的。因此,若開發者想在新線程中召喚槽函數,必須用工作對象worker-object模式,且切勿在在QThread子類中實現新的槽函數。
不同與隊列槽函數(或喚醒方法),QThread對象直接調用的方法將在調用該方法的線程上執行。當繼承QThread時,切記,構造於舊線程,run()執行與新線程。當兩個函數都訪問同一成員變量時,變量是
被兩個線程訪問的。一定驗證該變量安全後,再去做其他操作。

管理線程
當線程啓動和停止時,QThread通過信號通知你;isFinished()和isRunning()用於查詢線程狀態。exit()和quit()用來停止線程。緊急情況下,用terminate()可強行終止線程。這樣操作比較危險,一般情
況,不建議這麼幹。詳見terminate()和setTerminationEnabled()幫助文檔。

從Qt4.8開始,通過連接finished()信號和QObject::deleteLater(),線程結束後,線程內的對象可釋放了。
wait()用於阻塞調用線程,直到其他線程完成執行(或指定的時間到了,即超時)。

Qt5.0中,QThread開始提供靜態的、平臺無關的休眠函數:sleep()、msleep()和usleep(),分別用於解決秒、毫秒和微秒的休眠。


注意:
Qt是基於事件驅動的框架,故通常情況下,wait()和sleep()是不需要的。用等待finished()信號代替wait()。用QTimer代替代替sleep()。
靜態函數currentThreadld()和currentThread()返回正在執行線程的ID。前者返回平臺特定的線程ID。後者返回一個QThread指針。


類似linux下,用ps -L命令定義名稱一樣,爲了線程名稱可被選中,開啓線程前,使用setObjectName()設置其名稱。如果你不調用setObjectName(),線程的名稱就是該線程對象運行時的類名稱。例如,
在Mandelbat例子中的RenderThread,就是QThread子類的名稱。注意,Windows下的發佈版本本操作不可行。

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