1. 程式人生 > >槽機制的第五個引數Qt::ConnectionType

槽機制的第五個引數Qt::ConnectionType

轉載處:https://blog.csdn.net/kaida1234/article/details/79557348

原來對QThread的理解,就是重寫run(),曾經還一度搞不明白,到底它的槽屬於主執行緒還是子執行緒。
後來學了MFC,一度覺得MFC的機制比較人性化,起碼有工作執行緒和介面執行緒的用法,而不像QThread只有run是真正活在子執行緒裡面的。
而直到今天再次研究QThread,發現QThread有很好的功能void QObject::moveToThread(QThread*);
先上程式碼:

widget.h

 

[cpp] view plain

 copy

  1. #ifndef WIDGET_H  
  2. #define WIDGET_H  
  3.   
  4. #include <QWidget>  
  5. #include <QDebug>  
  6. #include <QThread>  
  7.   
  8. class QPushButton;  
  9.   
  10. namespace Ui {  
  11. class Widget;  
  12. }  
  13.   
  14. class myObject : public QObject  
  15. {  
  16.     Q_OBJECT  
  17. public:  
  18.     myObject() {}  
  19.     ~myObject() {}  
  20.   
  21. public slots:  
  22.     void first()   
  23.     {  
  24.         qDebug()<< QThread::currentThreadId();  
  25.     }  
  26.     void second()   
  27.     {  
  28.         qDebug()<< QThread::currentThreadId();  
  29.     }  
  30.     void third()   
  31.     {  
  32.         qDebug()<< QThread::currentThreadId();  
  33.     }  
  34. };  
  35.   
  36. class Widget : public QWidget  
  37. {  
  38.     Q_OBJECT  
  39.       
  40. public:  
  41.     explicit Widget(QWidget *parent = 0);  
  42.     ~Widget();  
  43.       
  44. private:  
  45.     Ui::Widget *ui;  
  46.     myObject *my;  
  47.     QPushButton *firstButton,*secondButton,*thirdButton,*selfButton;  
  48.   
  49. public slots:  
  50.     void onFirstPushed();  
  51.     void onSelfPushed();  
  52. };  
  53.   
  54. #endif // WIDGET_H  

 

 

widget.cpp

[cpp] view plain copy

  1. #include "widget.h"  
  2. #include "ui_widget.h"  
  3. #include <QVBoxLayout>  
  4. #include <QPushButton>  
  5.   
  6.   
  7. Widget::Widget(QWidget *parent) :  
  8.     QWidget(parent),  
  9.     ui(new Ui::Widget)  
  10. {  
  11.     ui->setupUi(this);  
  12.     my = new myObject;  
  13.     firstButton = new QPushButton(tr("firstBtn"), 0);  
  14.     connect(firstButton, SIGNAL(clicked()), this, SLOT(onFirstPushed()));  //this 是指當前物件
  15.     secondButton = new QPushButton(tr("secondBtn"), 0);  
  16.     connect(secondButton, SIGNAL(clicked()), my, SLOT(second()), Qt::DirectConnection);  //傳送者和接受者屬於不同的物件
  17.     thirdButton = new QPushButton(tr("thirdBtn"), 0);  
  18.     connect(thirdButton, SIGNAL(clicked()), my, SLOT(third()), Qt::QueuedConnection);  
  19.     selfButton = new QPushButton(tr("selfBtn"), 0);  
  20.     connect(selfButton, SIGNAL(clicked()), this, SLOT(onSelfPushed()));  
  21.   
  22.     QVBoxLayout *layout = new QVBoxLayout;  
  23.     layout->addWidget(firstButton);  
  24.     layout->addWidget(secondButton);  
  25.     layout->addWidget(thirdButton);  
  26.     layout->addWidget(selfButton);  
  27.   
  28.     this->setLayout(layout);  
  29.   
  30.     QThread *thread = new QThread;  
  31.     my->moveToThread(thread);  
  32.     connect(thread, SIGNAL(started()), my, SLOT(first()));  
  33.     thread->start();  
  34. }  
  35.   
  36. Widget::~Widget()  
  37. {  
  38.     delete ui;  
  39. }  
  40.   
  41. void Widget::onFirstPushed()   
  42. {  
  43.     my->first();  
  44. }  
  45.   
  46. void Widget::onSelfPushed()   
  47. {  
  48.     qDebug() << QThread::currentThreadId();  
  49. }  

此程式碼參考自http://blog.csdn.net/sydnash/article/details/7425947 ,在度娘找的各種文章中,這篇算是最簡潔易懂的,看了這篇再看其它都不成問題了。

    先說毫無疑問的兩個功能:一是程式開始,執行緒啟動,而my線上程中,my線上程中列印執行緒號;二是selfBtn,connect的槽函式完完全全屬於主執行緒的函式,列印主執行緒執行緒號。
    接下來是三個重點的按鈕,三個按鈕的連線方式是不一樣的。
    firstBtn連線的是主執行緒的槽函式,再在槽函式中執行first(),這樣first()是在主執行緒中呼叫的,打印出來的是主執行緒的ID;
    secondBtn直接連線myObject中的槽函式,使用的是Qt:DirectConnection直接連線模式,此模式下訊號與槽是同步的,訊號發出後,直接在訊號傳送者執行緒中呼叫槽函式,由於訊號是主執行緒發出的,因此列印的也是主執行緒的ID;
    thirdBtn也是直接連線myObject中的槽函式,但使用的是QT::QueuedConnection佇列連線模式,此模式下訊號與槽是非同步的,訊號發出後,會進入佇列中,直到控制權到了接收物件(my)屬於的執行緒(thread)的事件迴圈時,槽函式才被呼叫,因此此時列印的是子執行緒thread的執行緒ID。

    這裡重點介紹下connect的第五個引數Qt::ConnectionType。此引數可以有三種選擇Qt::AutoConnection、Qt::DirectConnection、Qt::QueuedConnection,分別是自動連線,直接連線和佇列連線。正如上面所說,直接連線是同步的(因為傳送者和接受者是同一個物件,一個主執行緒中,所以訊號一旦傳送,接受者立馬收到,相當於單執行緒同步),槽函式將和訊號同一執行緒,而佇列連線是非同步的(傳送者傳送訊號後,訊號存在資訊佇列中,當訊號迴圈進入接受物件的手時(先進先出),此時應該執行槽函式,相當於多執行緒非同步,此時接受者物件在訊號還沒有達到時,並不需要阻塞在那裡等待訊號來,而是可以處理其他事情),槽函式會屬於接收物件所屬執行緒。而自動連線是預設選項,將自動選擇直接連線和佇列連線之一。而什麼時候選擇什麼連線呢,傳送者和接收者處於相同執行緒,選擇直接連線;傳送者和接收者處於不同執行緒,使用佇列連線。(看過一篇很有趣的驗證文,有興趣可以參考http://www.cppblog.com/andreitang/archive/2011/08/26/154392.html

    對於上面程式碼而言QPushButton的兩個connect都屬於傳送者和接收者不在同一個執行緒,不使用第五個引數的情況下,是預設使用佇列連線的,而thread的connect屬於傳送者和接收者在同一執行緒,都是thread的執行緒,預設使用直接連線。
ps:需要注意的是,雖然在connect時,my由主執行緒生成,還沒moveToThread,還在主執行緒,但在執行時,訊號發出的時候,my已經在子執行緒了,因此自動連線下還是會選擇佇列連線。自動連線的選擇並不是在於物件生成的執行緒,而是在於物件所處的執行緒決定的。