我對Qt信號阻塞的理解是:
通過信號槽機制的方式調用函數
應用場景為多線程下子線程主動告知主線程某些信息並等待主線程處理完信息
下面來看代碼實現:
mythread.h/.cpp
1 //mythread.h : 2 class MyThread : public QThread 3 { 4 Q_OBJECT 5 public: 6 MyThread(); 7 ~MyThread(); 8 9 signals: 10 void slgTest(int iCount); 11 12 private: 13 QTimer* _pTimer; 14 void run(); 15 16 private slots: 17 void sltTimer(); 18 }; 19 20 //mythread.cpp 21 MyThread::MyThread(): 22 _pTimer(nullptr) 23 { 24 } 25 26 MyThread::~MyThread() 27 { 28 if(nullptr != _pTimer){ 29 _pTimer->stop(); 30 delete _pTimer; 31 } 32 this->quit(); 33 this->wait(); 34 } 35 36 void MyThread::run() 37 { 38 _pTimer = new QTimer; 39 connect(_pTimer,&QTimer::timeout,this,&MyThread::sltTimer,Qt::DirectConnection); 40 _pTimer->start(500); 41 this->exec(); 42 } 43 44 void MyThread::sltTimer() 45 { 46 static int s_iCount = 0; 47 s_iCount++; 48 qDebug()<<"Thread ID:"<<QThread::currentThreadId()<<" begin"; 49 emit slgTest(s_iCount); 50 qDebug()<<"Thread ID:"<<QThread::currentThreadId()<<" end"; 51 }
mainwindow.h/.cpp
1 //mainwindow.h 2 class mainWindow : public QWidget 3 { 4 Q_OBJECT 5 6 public: 7 explicit mainWindow(QWidget *parent = 0); 8 ~mainWindow(); 9 10 private: 11 Ui::mainWindow *ui; 12 MyThread _myThread; 13 14 private slots: 15 void sltReveice(int iCount); 16 }; 17 18 //mainwindow.cpp 19 mainWindow::mainWindow(QWidget *parent) : 20 QWidget(parent), 21 ui(new Ui::mainWindow) 22 { 23 ui->setupUi(this); 24 connect(&_myThread,SIGNAL(slgTest(int)),this,SLOT(sltReveice(int))); 25 _myThread.start(); 26 } 27 28 mainWindow::~mainWindow() 29 { 30 delete ui; 31 } 32 33 34 void mainWindow::sltReveice(int iCount) 35 { 36 qDebug()<<"Thread ID:"<<QThread::currentThreadId()<<" begin"; 37 QTime current_time =QTime::currentTime(); 38 qDebug()<<"time:"<<"minute:"<<current_time.minute()<<"second:"<<current_time.second()<<"msec:"<<current_time.msec(); 39 qDebug()<<"received data:"<<iCount; 40 qDebug()<<"Thread ID:"<<QThread::currentThreadId()<<" end"; 41 }
PS:此處代碼記錄一筆
1、QTimer的構造一定要在run()函數內部才能實現定時器觸發的槽函數在線程中構造,然后進行消息循環。
2、信號槽鏈連接一定要寫成:connect(_pTimer,&QTimer::timeout,this,&MyThread::sltTimer);
如果寫成connect(_pTimer,SIGNAL(*),this,SLOT(*));形式會提示鏈接不到槽函數,原因應該是run函數是重寫父類QThread的,run里的this指向的應該是QThread,而不是mythread。
3、線程中的定時器信號槽鏈接參數一定要是Qt::DirectConnection(信號槽連接參數可以參考這個博客:https://www.cnblogs.com/xupeidong/p/9487494.html)
4、一定要有Q_OBJECT,宏定義Q_OBJECT是任何實現信號、槽或屬性的強制性要求。
上面的代碼運行結果:
如上圖所示,兩個線程的數據交互沒有什么問題,但是如果我在主線程中sleep一下呢(模擬耗時函數)
在sltReveice()中添加usleep(1000);(linux下sleep頭文件:#include <unistd.h>)
運行結果:
如上圖所示開始嚴重不同步了
PS:此處會不斷觸發,信號會不斷排隊(應該僅在多線程的應用背景下有這種排隊的情況)
現在設置為阻塞connect(&_myThread,SIGNAL(slgTest(int)),this,SLOT(sltReveice(int)),Qt::BlockingQueuedConnection);
運行結果:
如上圖所示現在整齊排列了,子線程在等待主線程處理完成,回到開始說的“通過信號槽機制的方式調用函數”,也就是說相當於子線程調用了一個函數。
題外:上面提到了線程發送的信號不斷排隊的情況。
程序在上面程序中稍作改動
1、為mythread添加私有變量並在每次子線程定時器到的時候自增
2、mythread的私有變量在達到20的時候關閉定時器
3、為mythread添加獲取私有變量值的方法
此處不再展示代碼
代碼運行結果為:
如上圖所示定時器已經結束,但是主線程的槽函數依然在被觸發
此處處理方式是進入槽函數就斷開連接,退出函數時再連接(不再展示程序代碼和運行結果)
斷開連接函數:disconnect
PS斷開連接的方式:
1、大量斷開信號與槽函數
斷開所有連接到某一對象的信號槽:disconnect(myObject, 0, 0, 0); myObject->disconnect();
斷開所有連接到某一信號的信號槽:disconnect(myObject, SIGNAL(mySignal()), 0, 0); myObject->disconnect(SIGNAL(mySignal()));
斷開兩個對象之間的所有信號槽關系:disconnect(myObject, 0, myReceiver, 0);myObject->disconnect(myReceiver);
2、指定信號簡潔斷開
QMetaObject::Connection dis = connect(....); disconnect(dis);
sender()->disconnect();