Qt多線程 信號和槽以及C++11的綁定 及QMetaObject::invokeMethod
用C++11綁定信號和槽,能使代碼可讀性更高,靈活性更強
注:connect()中可聲明連接類型,默認缺省為AutoConnection
點擊滾動到 “連接類型” 介紹↓
:
#include#includeclass?MyWindow?:?public?QWidget { ????Q_OBJECT public: ????typedef??std::functionfuc1; ????MyWindow(QWidget?*parent?=?0); ????~MyWindow(); ????QPushButton?*??btn; ????QLabel*?label;??QVBoxLayout*?Vbox;? ????QGridLayout*?grid; ????void?click2(bool?checked); ????void?set(QString?s); public?slots: ????void?On_TestBtn_Cliked(); ????void?set2(QString?s); };
MyWindow::MyWindow(QWidget?*parent) ????:?QWidget(parent) { ????label?=?new?QLabel(QStringLiteral("你好"));? ????btn??=?new?QPushButton("Test");? ????grid?=?new?QGridLayout();? ????grid->addWidget(btn,0,0); ????Vbox?=?new?QVBoxLayout(); ????Vbox->addLayout(grid); ????Vbox->addWidget(label); ????setLayout(Vbox); ????fuc1?fu?=?std::bind(&MyWindow::click2,?this,?std::placeholders::_1); ????connect(btn,?&QPushButton::clicked,?this,?fu); } MyWindow::~MyWindow() { } void?MyWindow::set(QString?s) { ????qDebug()?<<"set:"<<?QThread::currentThreadId(); ????label->setText(s); } void?MyWindow::set2(QString?s) { ????qDebug()?<<?"set:"?<<?QThread::currentThreadId(); ????label->setText(s); } void?MyWindow::click2(bool?checked) { ????qDebug()?<<?"clcik2:"<start(); } void?MyWindow::On_TestBtn_Cliked() { ????qDebug()?<<?"click"; }
線程:
#pragma?once #include#includeclass?MyThread?: ????public?QThread { ????Q_OBJECT public: ????QObject*??window; ????void?run(); ????MyThread(QObject*?parent=NULL); ????~MyThread(); signals: ????void??print(QString); };
#include?"MyThread.h" #include?"MainWindow.h" MyThread::MyThread(QObject*?parent):QThread(parent) { ????window?=?parent; ????MyWindow*??w?=?(MyWindow*)parent;? ?std::functionfu?=?std::bind(&MyWindow::set,?w,?std::placeholders::_1); ????connect(this,?&MyThread::print,?w,fu);? } MyThread::~MyThread() { } void??MyThread::run() { ????qDebug()?<<?"Thread?Begin"; ????int?cout?=?0; ????while?(true) ????{ ????????qDebug()?<<?"MyThread::run:"?<<?QThread::currentThreadId(); ????????emit?print(QString::number(cout)); ????????Sleep(1); ????????cout++; ????} }
可以看到,print綁定的線程id和主線程相同,為線程安全
????connect(this,?&MyThread::print,?w,?[=](QString?s)?{ ????????w->set(s); ????????qDebug()?<<?"connet?fuc:"?<<?s?<<?"?"?<<?QThread::currentThreadId(); ????????//此時?線程id為主線程,可隨意調(diào)用主線程對象的界面操作 ????});
void??MyThread::run() { ????qDebug()?<<?"Thread?Begin"; ????int?cout?=?0; ????while?(true) ????{ ????????qDebug()?<<?"MyThread::run:"?<<?QThread::currentThreadId(); ??????QMetaObject::invokeMethod(w,?"set2",?Q_ARG(QString,?QString::number(cout))); ????????Sleep(1); ????????cout++; ????} }
MyThread::MyThread(QObject*?parent):QThread(parent) { ????window?=?parent; ????MyWindow*??w?=?(MyWindow*)parent; ????connect(this,?&MyThread::print,?w,?std::bind(&QLabel::setText,?w->label?,?std::placeholders::_1)); }
connect(this,?&MyThread::print,?w->label,?[=](QString?s) ????{ ????????//if(s.indexOf(xxxxx))?此處可以對字符串進行過濾 ????????qDebug()?<<?"Bind?Fuc:"?<<?QThread::currentThreadId(); ????????w->label->setText(s); ????});
或者
????connect(this,?&MyThread::print,?w,?[=](QString?s) ????{ ????????//if(s.indexOf(xxxxx))?此處可以對字符串進行過濾 ????????qDebug()?<<?"Bind?Fuc:"?<<?QThread::currentThreadId(); ????????w->label->setText(s); ????});
QMetaObject::invokeMethod
修改界面中的槽set2為:
void?MyWindow::set2(QString?s,?int?pid) { ????if?(pid?==(int)QThread::currentThreadId()) ????{ ????????qDebug()?<<?QStringLiteral("?此類型的線程不在主線程中::"?)<<?s; ????????qDebug()?<<?"id_1:"?<<?pid?<<?"?id_2:"?<<?QThread::currentThreadId(); ????} }
線程:
void??MyThread::run() { ????qDebug()?<<?"Thread?Begin"; ????int?cout?=?0; ????int?_pid; ????while?(true) ????{ ????????_pid?=?(int)QThread::currentThreadId(); ????????qDebug()?<<?"MyThread::run:"?<<?_pid; ???????//?emit?print(QString::number(cout)); ????????MyWindow*??w?=?(MyWindow*)window; ????????QMetaObject::invokeMethod(w,?"set2",?Qt::ConnectionType::QueuedConnection, ????????????Q_ARG(QString,?"QueuedConnection"),?Q_ARG(int,?_pid) ????????); ????????QMetaObject::invokeMethod(w,?"set2",?Qt::ConnectionType::AutoConnection, ????????????Q_ARG(QString,"AutoConnection"),?Q_ARG(int,?_pid) ????????); ????????QMetaObject::invokeMethod(w,?"set2",?Qt::ConnectionType::BlockingQueuedConnection,? ????????????Q_ARG(QString,?"BlockingQueuedConnection"),Q_ARG(int,?_pid) ????????); ????????QMetaObject::invokeMethod(w,?"set2",?Qt::ConnectionType::DirectConnection, ????????????Q_ARG(QString,?"DirectConnection"),?Q_ARG(int,?_pid) ????????); ????????QMetaObject::invokeMethod(w,?"set2",?Qt::ConnectionType::UniqueConnection,? ????????????Q_ARG(QString,?"UniqueConnection"),?Q_ARG(int,?_pid) ????????); ????????Sleep(1); ????????cout++; ????} }
可見,DirectConnection連接類型的線程ID與主線程不同,與線程相同,不是線程安全,其他連接類型暫時無法找到方法測試,有例子的朋友可以跟我交流下,謝謝
是官方說明的連接類型,翻譯
說明:信號:發(fā)送者 ?槽:接受者 ?信號和槽所在的線程是創(chuàng)建他們的線程,而不是調(diào)用connnet的時候所在的線程 AutoConnection
如果接收方住在線程發(fā)出信號,使用Qt::DirectConnection。否則,使用Qt::QueuedConnection。連接類型發(fā)送信號時決定。
解釋:
如果接收方住在線程發(fā)出信號,使用Qt::DirectConnection。否則,使用Qt::QueuedConnection。連接類型發(fā)送信號時決定。
也就是說,自動判斷,如果信號和槽在同一個線程,就調(diào)用Qt::DirectConnection,否則調(diào)用Qt::QueuedConnection
DirectConnection
調(diào)用插槽立即發(fā)出信號時。槽是在信號線程中執(zhí)行的。
QueuedConnection
可以理解為異步?
當槽發(fā)送給調(diào)用接收事件循環(huán)的線程時,槽在接收者的線程中執(zhí)行。
也就是說,此連接類型,只管把信號發(fā)送到槽所在的線程事件中,不會等待槽所在的線程事件處理完畢
,槽所在線程事件循環(huán)當處理到此信號時,才會執(zhí)行相應(yīng)操作
BlockingQueuedConnection
可以理解為同步,阻塞當前線程直到同步?
當槽發(fā)送給調(diào)用接收事件循環(huán)的線程時,槽在接收者的線程中執(zhí)行。
也就是說,此連接類型,不但把信號發(fā)送到槽所在的線程事件中,而且會等待槽所在的線程事件處理完畢
,槽所在線程事件循環(huán)當處理到此信號時,才會執(zhí)行相應(yīng)操作,信號所在的線程才會繼續(xù)下一行代碼
UniqueConnection
資料太少,不知道此類型的大概用途。。。
這是一個標志,可以結(jié)合上述任何一個連接類型,使用逐位或。當Qt:UniqueConnection,QObject:connect()將會失敗如果連接已經(jīng)存在(即如果相同的信號已經(jīng)連接到同一個槽同一雙對象)。這個標志是在Qt 4.6中引入的。