程序中,一個QThread對象能管理控制一個線程。線程啓動於run()。默認下,調用exec()後,run()纔在線程中開啓一個Qt事件循環。
用QObject::moveToThread()將工作對象移動到指定線程。
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
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下的發佈版本本操作不可行。