本篇博客將深入討論信號與槽,重點討論信號與槽的連接方式。信號與槽的連接方式還有什么值得我們注意的地方嗎?
之前是如何連接信號與槽的呢?
通過connect函數將指定的信號連接到指定的槽函數上面,接下來將要發生的事情就是信號一旦被發射,相應的槽函數就會被調用。這是我們最直觀的認識。
每次調用connect函數時,都省略了這個函數的第5個參數,使我們誤以為connect函數就只有4個參數。connect函數其實有5個參數,最后一個參數就是指定信號與槽的連接方式。然而,信號與槽的連接方式也是Qt中多線程編程的難點之一了。
深入信號與槽的連接方式
-Qt::DirectConnection(立即調用)
-Qt::QueuedConnection(異步調用)
-Qt::BlockingQueuedConnection(同步調用)
-Qt::AutoConnection(默認連接)
-Qt::UniqueConnection(單一連接)
小知識
bool connect(const QObject* sender, const char* signal, const QObject* receiver, const char* method, Qt::ConnectType type = Qt::AutoConnection);
信號與槽的連接方式決定槽函數調用時的相關行為。
知識回顧
-每一個線程都有自己的事件隊列(一定要熟記,Qt中的每一個線程都有自己的事件隊列)
-線程通過事件隊列接收信號
-信號在事件循環中被處理
在發射信號的時候,好像並沒有指定發射到哪個線程里面去。那么Qt這個平台是如何知道將發射了的信號投遞到哪個線程的事件隊列中的呢?
依據的就是對象的依附性。看下面的示意圖:
在線程1中要發射1個我們自定義的信號,發射之后就進入某個線程的事件隊列中去了,那如何決定哪個線程的事件隊列呢?
其實就是由對象的依附性決定的,
在發射信號之前,通過connect函數將signal信號連接到obj里面的slot函數上,根據線程的依附性就可以知道,emit signal這條語句執行之后,
signal信號就直接進入到線程2的事件隊列,在事件隊列中又能干什么呢?什么都干不了。因此就要求線程2開啟事件循環,線程2一旦開啟事件循環,就會從事件隊列中取發射過來的信號了。之后就根據連接調用obj中的slot槽函數了。
信號與槽的連接方式:
1.立即調用
信號與槽的連接方式為立即調用,代碼示例如下:
MyObject.h

#ifndef MYOBJECT_H #define MYOBJECT_H #include <QObject> class MyObject : public QObject { Q_OBJECT public: explicit MyObject(QObject* parent = 0); signals: protected slots: 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: explicit TestThread(); signals: void testSignal(); }; #endif // TESTTHREAD_H
MyObject.cpp

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

#include "TestThread.h" #include <QDebug> TestThread::TestThread() { } void TestThread::run() { qDebug() << "void TestThread::run() -- begin tid = " << currentThreadId(); for(int i=0; i<3; i++) { qDebug() << "void TestThread::run() i = " << i; sleep(1); } emit testSignal(); qDebug() << "void TestThread::run() -- end" ; }
main.cpp

#include <QCoreApplication> #include <QDebug> #include <QThread> #include "MyObject.h" #include "TestThread.h" void direct_connection() { static TestThread t; static MyObject m; QObject::connect(&t,SIGNAL(testSignal()),&m,SLOT(testSlot()),Qt::DirectConnection); t.start(); //啟動線程 t.wait(5*1000); //等待5s t.quit(); //結束t中的線程循環 } int main(int argc, char* argv[]) { QCoreApplication a(argc, argv); qDebug() << "main tid = " << QThread::currentThreadId(); direct_connection(); return a.exec(); }
運行結果如下:
根據運行結果進行深入剖析:
通過打印的語句進行分析,主線程的id是0xc0c,由於調用了direct_connect,所以啟動了一個子線程,子線程的id是0x1784,接線來延續3s中后發射信號,發射信號后槽函數必然會被調用。調用的槽函數在哪個線程中被執行的呢?通過打印的線程id可知,它是在子線程中被執行的。
此時你有沒有感到奇怪,對象m依附於主線程,根據之前說過的,槽函數顯然在主線程中被調用執行,但是我們指定了信號與槽的連接方式為直接調用。
導致的行為就是在發射信號的地方直接調用了槽函數,換句話說emit testSignal()就直接替換成了槽函數的調用語句testSlot();
2.異步調用
main.cpp

#include <QCoreApplication> #include <QDebug> #include <QThread> #include "MyObject.h" #include "TestThread.h" void queued_connection() { static TestThread t; static MyObject m; QObject::connect(&t,SIGNAL(testSignal()),&m,SLOT(testSlot()),Qt::QueuedConnection); t.start(); //啟動線程 t.wait(5*1000); //等待5s t.quit(); //結束t中的線程循環 } int main(int argc, char* argv[]) { QCoreApplication a(argc, argv); qDebug() << "main tid = " << QThread::currentThreadId(); queued_connection(); return a.exec(); }
根據運行結果進行深入剖析:
主線程id是0x754,子線程的id是0xd74,子線程先延遲3s中,然后再發射信號。從運行結果看,槽函數並沒有被立即調用,沒有在子線程中調用槽函數,那么槽函數究竟是在哪調用的呢?
顯然是在主線程中被調用的,那么為什么會在主線程中被調用呢?
根據對象的依附性可以知道,m對象依附於主線程,發射的信號就進入了主線程的事件循環隊列中,又因為這個地方的連接是QueuedConnection,所以說進入到主線程的事件循環隊列中后,子線程就不管了,接着就向下執行。什么時候調用槽函數是由主線程決定的。
3.同步調用

#include <QCoreApplication> #include <QDebug> #include <QThread> #include "MyObject.h" #include "TestThread.h" void blocking_queued_connection() { static TestThread t; static MyObject m; QObject::connect(&t,SIGNAL(testSignal()),&m,SLOT(testSlot()),Qt::BlockingQueuedConnection); t.start(); //啟動線程 t.wait(5*1000); //等待5s t.quit(); //結束t中的線程循環 } int main(int argc, char* argv[]) { QCoreApplication a(argc, argv); qDebug() << "main tid = " << QThread::currentThreadId(); blocking_queued_connection(); return a.exec(); }
4.默認連接
5.單一連接
小結: