令人不解的問題:
當槽函數是線程類的成員時,為什么依然不在本線程內被調用執行?
隱藏的問題:
對象依附於哪一個線程?
對象的依附性與槽函數執行的關系?
對象的依附性是否可以改變?
對象依附於哪個線程?
默認情況下,對象依附於自身被創建的線程
例如:對象在主線程(main()函數)中被創建,則依附於主線程
int main(int argc, char* argv[]) { //...
TestThread t; //依附於主線程
MyObject m; //依附於主線程
}
對象的依附性與槽函數執行的關系?
默認情況下,槽函數在其所依附的線程中被調用執行
int main(int argc, char* argv[]) { //...
TestThread t; //依附於主線程
MyObject m; //依附於主線程 //下面連接中的槽函數都在主線程中被調用執行
QObject::connect(&t,SIGNAL(started()),&m,SLOT(getStarted())); QObject::connect(&t,SIGNAL(testSignal()),&m,SLOT(testSlot())); }
對象的依附性是否可以改變?
QObject::moveToThread用於改變對象的線程的依附性,使得對象的槽函數在依附的線程中被調用執行
int main(int argc, char* argv[]) { //...
TestThread t; //依附於主線程
MyObject m; //依附於主線程 //改變對象m的線程的依附性,使其依附於線程t
m.moveToThread(&t) }
對象的依附性
MyObject.h

#ifndef MYOBJECT_H #define MYOBJECT_H #include <QObject> class MyObject : public QObject { Q_OBJECT public: MyObject(); protected slots: void getStarted(); void testSlot(); }; #endif // MYOBJECT_H
TestThread.h

#ifndef TESTTHREAD_H #define TESTTHREAD_H #include <QThread> class TestThread : public QThread { Q_OBJECT protected: void run(); public: TestThread(); signals: void testSignal(); protected slots: void testSlot(); }; #endif // TESTTHREAD_H
MyObject.cpp

#include "MyObject.h" #include <QObject> #include <QThread> #include <QDebug> MyObject::MyObject() { } void MyObject::getStarted() { qDebug() <<"void MyObject::getStarted()tid = " << QThread::currentThreadId() ; } void MyObject::testSlot() { qDebug() << "void MyObject::testSlot() " << QThread::currentThreadId() ; }
TestThread.cpp

#include "TestThread.h" #include <QDebug> TestThread::TestThread() { connect(this,SIGNAL(testSignal()),this,SLOT(testSlot())); } void TestThread::run() { qDebug() << "void TestThread::run() begin...tid = " << currentThreadId(); for(int i=0; i<10; i++) { qDebug() << "void TestThread::run() i = " << i; sleep(1); } emit testSignal(); //發射的信號誰來接收呢,可以在構造函數中將信號和槽函數進行關聯。 qDebug() << "void TestThread::run() end..."; } void TestThread::testSlot() { qDebug() << "void TestThread::testSlot() tid = " << currentThreadId(); }
main.cpp

#include <QCoreApplication> #include <QThread> #include <QDebug> #include "TestThread.h" #include "MyObject.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() << "main tid() " << QThread::currentThreadId(); TestThread t; MyObject m; QObject::connect(&t,SIGNAL(started()),&m,SLOT(getStarted())); QObject::connect(&t,SIGNAL(testSignal()),&m,SLOT(testSlot())); m.moveToThread(&t); t.start(); return a.exec(); }
課程中使用的是Qt4,MyObject相關的槽函數沒有被調用,但是使用Qt5.4,MyObject相關的槽函數被調用了。
下面分析沒有被調用的情況:
問題:
實驗中對象m的槽函數為什么沒有全部被執行?
線程中的事件循環
信號與槽的機制需要事件循環的支持
QThread類中提供的exec()函數用於開啟線程的事件循環
只有開啟事件循環,槽函數才能在信號發送后被調用
信號的發送是隨時隨地都可以完成的,發送完成后,信號就到事件隊列中去了。信號進入事件隊列中去有什么用呢?
沒人理它,它什么用都沒有。如何處理事件隊列呢?此時事件循環就派上用場了。
但凡通過exec開啟了事件循環,就會不停的從事件隊列中取信號,取到信號后就會去判斷該信號有沒有關聯相關的槽函數,
如果有對應的槽函數,則調用相應的槽函數。
想要槽函數在指定的線程中被調用,需要在指定的線程中調用exec函數,開啟事件循環。
小結論:
前提條件
對象依附的線程開啟了事件循環
后置結果
對象中的槽函數在依附的線程中被調用執行
只需要在TestThread.cpp的run函數中加上exec函數即可

void TestThread::run() { qDebug() << "void TestThread::run() begin...tid = " << currentThreadId(); for(int i=0; i<10; i++) { qDebug() << "void TestThread::run() i = " << i; sleep(1); } emit testSignal(); //發射的信號誰來接收呢,可以在構造函數中將信號和槽函數進行關聯。 exec(); qDebug() << "void TestThread::run() end..."; }
從打印結果看,與上面沒有使用exec的執行結果並無不同,很可能是因為版本不同造成的。
從打印結果看,MyObject的兩個槽函數都被調用了,且是在依附於t的那個線程。但是t的槽函數還是依附於主線程,我也想讓t的槽函數依附t,怎么操作?非常簡單,只需要在main.cpp中的main函數中,加入t.moveToThread(&t)即可,如下所示:

int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() << "main tid() " << QThread::currentThreadId(); TestThread t; MyObject m; QObject::connect(&t,SIGNAL(started()),&m,SLOT(getStarted())); QObject::connect(&t,SIGNAL(testSignal()),&m,SLOT(testSlot())); m.moveToThread(&t); t.moveToThread(&t); t.start(); return a.exec(); }
研究槽函數的具體執行線程有什么意義?
當信號的發送與對應槽函數的執行在不同線程中,可能產生臨界資源的競爭問題
比如說,在run函數對某一個臨界資源進行修改,在槽函數中也對臨界資源進行修改,槽函數的調用是在另一個線程中完成的,此時調用槽函數的線程和本身的線程就可能產生競爭問題。