槽機制的第五個引數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
- #ifndef WIDGET_H
- #define WIDGET_H
- #include <QWidget>
- #include <QDebug>
- #include <QThread>
- class QPushButton;
- namespace Ui {
- class Widget;
- }
- class myObject : public QObject
- {
- Q_OBJECT
- public:
- myObject() {}
- ~myObject() {}
- public slots:
- void first()
- {
- qDebug()<< QThread::currentThreadId();
- }
- void second()
- {
- qDebug()<< QThread::currentThreadId();
- }
- void third()
- {
- qDebug()<< QThread::currentThreadId();
- }
- };
- class Widget : public QWidget
- {
- Q_OBJECT
- public:
- explicit Widget(QWidget *parent = 0);
- ~Widget();
- private:
- Ui::Widget *ui;
- myObject *my;
- QPushButton *firstButton,*secondButton,*thirdButton,*selfButton;
- public slots:
- void onFirstPushed();
- void onSelfPushed();
- };
- #endif // WIDGET_H
widget.cpp
[cpp] view plain copy
- #include "widget.h"
- #include "ui_widget.h"
- #include <QVBoxLayout>
- #include <QPushButton>
- Widget::Widget(QWidget *parent) :
- QWidget(parent),
- ui(new Ui::Widget)
- {
- ui->setupUi(this);
- my = new myObject;
- firstButton = new QPushButton(tr("firstBtn"), 0);
- connect(firstButton, SIGNAL(clicked()), this, SLOT(onFirstPushed())); //this 是指當前物件
- secondButton = new QPushButton(tr("secondBtn"), 0);
- connect(secondButton, SIGNAL(clicked()), my, SLOT(second()), Qt::DirectConnection); //傳送者和接受者屬於不同的物件
- thirdButton = new QPushButton(tr("thirdBtn"), 0);
- connect(thirdButton, SIGNAL(clicked()), my, SLOT(third()), Qt::QueuedConnection);
- selfButton = new QPushButton(tr("selfBtn"), 0);
- connect(selfButton, SIGNAL(clicked()), this, SLOT(onSelfPushed()));
- QVBoxLayout *layout = new QVBoxLayout;
- layout->addWidget(firstButton);
- layout->addWidget(secondButton);
- layout->addWidget(thirdButton);
- layout->addWidget(selfButton);
- this->setLayout(layout);
- QThread *thread = new QThread;
- my->moveToThread(thread);
- connect(thread, SIGNAL(started()), my, SLOT(first()));
- thread->start();
- }
- Widget::~Widget()
- {
- delete ui;
- }
- void Widget::onFirstPushed()
- {
- my->first();
- }
- void Widget::onSelfPushed()
- {
- qDebug() << QThread::currentThreadId();
- }
此程式碼參考自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已經在子執行緒了,因此自動連線下還是會選擇佇列連線。自動連線的選擇並不是在於物件生成的執行緒,而是在於物件所處的執行緒決定的。