[Qt學習篇]Qthread實現多執行緒操作
一、QThread類概述
QThread類為使用者管理多執行緒提供了一種平臺無關的途徑。
#include <QThread>
繼承自QObject類
二、QThread類詳述
QThread物件在程式內部進行控制執行緒的管理,QThread起始於run()函式額執行。預設情況下,run()通過呼叫exec()啟動事件迴圈(event loop),並在執行緒內部執行Qt 的事件迴圈。
以下示例通過QObject::moveToThread()
class Worker : public QObject { Q_OBJECT QThread workerThread; public slots: void doWork(const QString str) { // ... 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, SIGNAL(finished()), worker, SLOT(deleteLater())); connect(this, SIGNAL(operate(QString)), worker, SLOT(doWork(QString))); connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString))); workerThread.start(); } ~Controller() { workerThread.quit(); workerThread.wait(); } public slots: void handleResults(const QString &); signals: void operate(const QString &); };
Worker槽中的程式碼以後就在一個獨立的執行緒中執行。你可以按自己意願,把Worker槽(slots)連線到任何物件的訊號(signal)上。得益於佇列連線(queued connections) 機制(一個事件被派發到接收者所線上程的事件迴圈,當事件被傳遞,相應的槽隨之被啟用),在不同物件間進行訊號和槽的連線是安全的。
另外一種實現多執行緒的方式是繼承QThread類,並重新實現run()函式,例如:
class Thread : public QThread { Q_OBJECT public: Thread(); void setMessage(const QString &message); void stop(); protected: void run(); private: QString messageStr; volatile bool stopped; QUdpSocket *udpSpcket; }; class ThreadDialog : public QDialog { Q_OBJECT public: ThreadDialog(QWidget *parent = 0); protected: void closeEvent(QCloseEvent *event); private slots: void startOrStopThreadA(); void startOrStopThreadB(); private: Thread threadA; Thread threadB; QPushButton *threadAButton; QPushButton *threadBButton; };
注意:
1、當run()函式返回的以後,執行緒會退出(exit)。如果沒有呼叫exec()函式,該執行緒中不存在任何執行狀態的事件迴圈(event loop)。
2、重要的是,QThread物件通常存在於(lives in)建立他的執行緒中,而不是它管理的執行緒中。這意味著,QThread的槽是在它存在的執行緒(home thread)的上下文中被執行,而不是它管理的執行緒的上下文中。基於這一點,在QThread子類中實現新的槽是容易出錯所以是不被提倡的。
3、如果使用其他方法(technique)在不同物件間進行互動,而不是佇列性的訊號-槽(signal/slot)機制,那麼在多執行緒程式中就要預先警惕可能出現的問題。
4、GUI物件的執行緒必須存在於主執行緒(main thread)中。
三、管理QThread
Managing threads
當執行緒開始執行(started())、執行完畢(finished())、終止(terminated())的時候,QThread通過訊號()進行通知;反過來,也可以呼叫isFinished()和isRunning()查詢執行緒的狀態。
要想停止(stop)執行緒,可以通過呼叫exit()或quit()實現,在極端情況下,可以呼叫terminate()強制終止一個執行狀態的執行緒——但是這樣比較危險,並不提倡,使用setTerminationEnabled()函式可以使能/禁止terminate()函式。
Qt 4.8以後,通過把finished()訊號連線到QObject::deleteLater()槽上,可以線上程結束後刪除(deallocate)物件。
使用wait()可以阻塞呼叫執行緒,直到其他執行緒執行結束(也可以指定一個超時時間)。
靜態(static)函式currentThreadId()和currentThread()返回當前執行執行緒的的識別符號(identifiers),
線上程啟動前呼叫setObjectName()可以給執行緒指定一個名字,作為和其他執行緒的區別標識。如果沒有呼叫setObjectName(),將以執行緒物件執行時型別的類名作為執行緒的名字。比如,下面例子中,執行緒的名字是RenderThread:
class RenderThread : public QThread
{
Q_OBJECT
public:
RenderThread(QObject *parent = 0);
~RenderThread();
void render(double centerX, double centerY, double scaleFactor, QSize resultSize);
signals:
void renderedImage(const QImage &image, double scaleFactor);
protected:
void run();
private:
uint rgbFromWaveLength(double wave);
QMutex mutex;
QWaitCondition condition;
};
注意:這種規則目前不適用於在Windows閃編譯的release。QThread類提供了static型別,平臺無關的sleep函式:sleep(),msleep(),usleep(),經度分別是秒、毫秒、微秒。
注意:一般來講,wait()和sleep()應該是不必要的(unnecessary),因為Qt是基於事件驅動(event-driven)的架構。使用wait()的時候,考慮下finished(),sleep()時,考慮下QTimer。
四、QThread優先順序
enum Qthread::Priority
常量 |
值 |
描述 |
QThread::IdlePriority |
0 |
沒有其他執行緒執行時才進行排程 |
QThread::LowestPriority |
1 |
不比LowPriority排程頻繁 |
QThread::LowPriority |
2 |
不比NormalPriority排程頻繁 |
QThread::NormalPriority |
3 |
作業系統的預設優先順序 |
QThread::HighPriority |
4 |
比NormalPriority排程頻繁 |
QThread::HighestPriority |
5 |
比HighPriority排程頻繁 |
QThread::TimeCriticalPriority |
6 |
儘可能頻繁的進行排程 |
QThread::InheritPriority |
7 |
使用和建立自己的執行緒同樣的優先順序,這是預設屬性 |