多執行緒中的訊號與槽(中)
令人不解的問題:
當槽函式是執行緒類的成員時,為什麼依然不在本執行緒內被呼叫執行?
隱藏的問題:
物件依附於哪一個執行緒?
物件的依附性與槽函式執行的關係?
物件的依附性是否可以改變?
物件依附於哪個執行緒?
預設情況下,物件依附於自身被建立的執行緒
例如:物件在主執行緒(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 publicView Code: 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_HView Code
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() ; }View Code
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(); }View Code
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(); }View Code
課程中使用的是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..."; }View Code
從列印結果看,與上面沒有使用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(); }View Code
研究槽函式的具體執行執行緒有什麼意義?
當訊號的傳送與對應槽函式的執行在不同執行緒中,可能產生臨界資源的競爭問題
比如說,在run函式對某一個臨界資源進行修改,在槽函式中也對臨界資源進行修改,槽函式的呼叫是在另一個執行緒中完成的,此時呼叫槽函式的執行緒和本身的執行緒就可能產生競爭問題。