QT:moveToThread與訊號與槽機制的第五個引數Qt::ConnectionType
阿新 • • 發佈:2019-01-01
原來對QThread的理解,就是重寫run(),曾經還一度搞不明白,到底它的槽屬於主執行緒還是子執行緒。
後來學了MFC,一度覺得MFC的機制比較人性化,起碼有工作執行緒和介面執行緒的用法,而不像QThread只有run是真正活在子執行緒裡面的。
而直到今天再次研究QThread,發現QThread有很好的功能void QObject::moveToThread(QThread*);
先上程式碼:
先說毫無疑問的兩個功能:一是程式開始,執行緒啟動,而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已經在子執行緒了,因此自動連線下還是會選擇佇列連線。自動連線的選擇並不是在於物件生成的執行緒,而是在於物件所處的執行緒決定的。
好了總結完畢,準備將執行緒加入到專案中。。。
End
後來學了MFC,一度覺得MFC的機制比較人性化,起碼有工作執行緒和介面執行緒的用法,而不像QThread只有run是真正活在子執行緒裡面的。
而直到今天再次研究QThread,發現QThread有很好的功能void QObject::moveToThread(QThread*);
先上程式碼:
widget.h
widget.cpp#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
#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())); 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(); }
先說毫無疑問的兩個功能:一是程式開始,執行緒啟動,而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已經在子執行緒了,因此自動連線下還是會選擇佇列連線。自動連線的選擇並不是在於物件生成的執行緒,而是在於物件所處的執行緒決定的。
好了總結完畢,準備將執行緒加入到專案中。。。
End