Qt 多執行緒
QThread 是Qt中一個對執行緒支援的核心的底層類。每個執行緒物件代表了一個執行的執行緒。
POINT 1:QThread類的例項與普通類的例項沒什麼不同,只是執行著的run()函式會不同。
例1:
class MThread :public QThread { public: MThread(); ~MThread(); void run(); void foo(); ... }; class MDialog :public QDialog { ... MThread*mythread; }; MDialog::MDialog() { mythread = new MThread; ... }
需要注意的是,在QT中,QThread物件的例項mythread是屬於建立它的執行緒(執行緒A,即MDialog所在的執行緒) 的,mythread的所有程式程式碼與資料都放在與MDialog相同的空間中.這時的mythread,就像任何普通的自己定義的類的例項一樣.但是在 呼叫mythread->start()之後,mythread的run()函式中的程式碼會在新的執行緒(執行緒B)中執行.在run()函式中宣告的 變數\例項化的物件,都屬於執行緒B.
當emit sigDialogSignal()時,是會在MDialog所在的執行緒A中執行的.因為mythread與MDialog同屬於一個執行緒, 這時thread可以看做一個普通類的例項.另外,因為connect函式的連線方式預設是自動連線,而對同屬於一個純種的兩個物件,自動連線會使用直接連線,即slot在發出signal的執行緒中立即執行.
例2:
#include "mthread.h" #include <QDebug> MThread::MThread(QObject *parent) : QThread(parent) { myTimer.start(1); connect(&myTimer, SIGNAL(timeout()), this, SLOT(slotPrint())); }
MThread::~MThread() { }
void MThread::run() { for (int i = 0; i < 100; ++i) { for (int j = 0 ; j < 10000; ++j) { qDebug()<<"---------"<<i; } } exec(); }
void MThread::slotPrint() { qDebug()<<"=============================="; }
執行後出現:
---------9
==============================================================
---------9
不能誤以為:在一個QThread類的派生類中,run()函式中的語句在執行時,可能被本執行緒定時器超時slot中斷. (錯誤)
事實上,slotPrint()在建立MThread的例項的執行緒中執行.
POINT 2:執行緒B中的物件要想接收執行緒A中的物件發來的signal, 必須進入exec(), 如在exec()前有死迴圈, 沒有進入exec(), 則執行緒B中的物件不會收到signal.
void MThread::run() { while(1) { dosomething(); //此迴圈永不退出 } exec(); //如果此事件迴圈不能進入,剛此執行緒不會收到任何signal }
POINT 3:執行緒A中的指標可指向執行緒B中建立的物件例項,這個例項屬於執行緒B. 指標僅僅是一個地址, 而物件例項的變數/程式碼等都屬於執行緒B.
例1:
class MThread : public QThread { Q_OBJECT public: MThread(QObject *parent = 0); ~MThread(); void run(); MPrint *mprint; }; void MThread::run() { mprint = new MPrint; exec(); } //如此宣告,mprint所指向的物件屬於另一個執行緒.
例2:
class MThread : public QThread { Q_OBJECT public: MThread(QObject *parent = 0); ~MThread(); void run(); MPrint *mprint; private: QTimer *myTimer; private slots: void slotPrint(); void testFoo(); }; void MThread::run() { myTimer = new QTimer; mprint = new MPrint; myTimer->setInterval(100); connect(myTimer, SIGNAL(timeout()), this, SLOT(testFoo()), Qt::DirectConnection); QTimer::singleShot(0, myTimer,SLOT(start())); exec();
}
上這樣寫run(),myTimer在run()中new,即myTimer這個指標屬於舊執行緒,但myTimer所指向的QTimer例項的實體在新的執行緒中,testFoo()會在新執行緒中執行。
例3:
void MThread::run() { QTimer myTimer; mprint = new MPrint; myTimer.setInterval(100); connect(&myTimer, SIGNAL(timeout()), this, SLOT(testFoo()), Qt::DirectConnection); QTimer::singleShot(0, &myTimer,SLOT(start())); //testFoo(); exec(); }
以上這樣寫run(),myTimer在run()中宣告,即myTimer屬於新的執行緒,testFoo()也會在新執行緒中執行。
例4:
class MThread : public QThread { Q_OBJECT public: MThread(QObject *parent = 0); ~MThread(); void run(); MPrint *mprint; private: QTimer myTimer; private slots: void slotPrint(); void testFoo(); }; void MThread::run() { mprint = new MPrint; myTimer.setInterval(100); connect(&myTimer, SIGNAL(timeout()), this, SLOT(testFoo())); QTimer::singleShot(0, &myTimer,SLOT(start())); //testFoo(); exec(); }
以上這樣寫run(),testFoo()會在建立myTimer的老執行緒中執行.因為可以看到,mytimer和this(即mythread),都是在同一個執行緒中,只是在另一個執行緒中(run()),做了connect操作.
要注意的是,在執行緒B中啟動執行緒A中的一個定時器,不能使用myTimer.start(),這樣啟動不了定時器.而應使用signal來觸發start()這個slot. QTimer::singleShot(0, &myTimer,SLOT(start()));
POINT 5:slot不會中斷同執行緒中的slot。
例1:
#include "mthread.h" #include <QDebug> MThread::MThread(QObject *parent) : QThread(parent) { myTimer.start(1); connect(&myTimer, SIGNAL(timeout()), this, SLOT(slotPrint())); } MThread::~MThread() { } void MThread::run() { exec(); } void MThread::slotPrint() { qDebug()<<"==========================="; for (int i = 0; i < 100; ++i) { for (int j = 0 ; j < 10000; ++j) { qDebug()<<"---------"<<i; } } }
slotPrint()函式執行完之後才會退出,說明slot不會中斷slot,一個slot在執行完之後才會執行下一個slot.
注意:slotPrint()在建立MThread例項的執行緒中執行(主執行緒).而不是使用thread->start()創建出的那個執行緒(子執行緒)。
例2:
#include "mthread.h" #include <QDebug> MThread::MThread(QObject *parent) : QThread(parent) { myTimer.start(1); connect(&myTimer, SIGNAL(timeout()), this, SLOT(slotPrint())); } MThread::~MThread() { } void MThread::run() { testFoo(); exec(); } void MThread::slotPrint() { qDebug()<<"======================="; } void MThread::testFoo() { for (int i = 0; i < 100; ++i) { for (int j = 0 ; j < 10000; ++j) { qDebug()<<"---------"<<i; } } }
以上程式碼中,slotPrint()與testFoo()會在兩個不同的執行緒中執行.
注意:只有gui執行緒即主執行緒可以呼叫gui相關的函式,其他執行緒不可以,例如非gui執行緒顯示提示對話方塊是不允許的。