Qt多線程 信號(hào)和槽以及C++11的綁定 及QMetaObject::invokeMethod
用C++11綁定信號(hào)和槽,能使代碼可讀性更高,靈活性更強(qiáng)
注:connect()中可聲明連接類型,默認(rèn)缺省為AutoConnection
點(diǎn)擊滾動(dòng)到 “連接類型” 介紹↓
:
#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(); ????????//此時(shí)?線程id為主線程,可隨意調(diào)用主線程對(duì)象的界面操作 ????});
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))?此處可以對(duì)字符串進(jìn)行過濾 ????????qDebug()?<<?"Bind?Fuc:"?<<?QThread::currentThreadId(); ????????w->label->setText(s); ????});
或者
????connect(this,?&MyThread::print,?w,?[=](QString?s) ????{ ????????//if(s.indexOf(xxxxx))?此處可以對(duì)字符串進(jìn)行過濾 ????????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與主線程不同,與線程相同,不是線程安全,其他連接類型暫時(shí)無法找到方法測(cè)試,有例子的朋友可以跟我交流下,謝謝
是官方說明的連接類型,翻譯
說明:信號(hào):發(fā)送者 ?槽:接受者 ?信號(hào)和槽所在的線程是創(chuàng)建他們的線程,而不是調(diào)用connnet的時(shí)候所在的線程 AutoConnection
如果接收方住在線程發(fā)出信號(hào),使用Qt::DirectConnection。否則,使用Qt::QueuedConnection。連接類型發(fā)送信號(hào)時(shí)決定。
解釋:
如果接收方住在線程發(fā)出信號(hào),使用Qt::DirectConnection。否則,使用Qt::QueuedConnection。連接類型發(fā)送信號(hào)時(shí)決定。
也就是說,自動(dòng)判斷,如果信號(hào)和槽在同一個(gè)線程,就調(diào)用Qt::DirectConnection,否則調(diào)用Qt::QueuedConnection
DirectConnection
調(diào)用插槽立即發(fā)出信號(hào)時(shí)。槽是在信號(hào)線程中執(zhí)行的。
QueuedConnection
可以理解為異步?
當(dāng)槽發(fā)送給調(diào)用接收事件循環(huán)的線程時(shí),槽在接收者的線程中執(zhí)行。
也就是說,此連接類型,只管把信號(hào)發(fā)送到槽所在的線程事件中,不會(huì)等待槽所在的線程事件處理完畢
,槽所在線程事件循環(huán)當(dāng)處理到此信號(hào)時(shí),才會(huì)執(zhí)行相應(yīng)操作
BlockingQueuedConnection
可以理解為同步,阻塞當(dāng)前線程直到同步?
當(dāng)槽發(fā)送給調(diào)用接收事件循環(huán)的線程時(shí),槽在接收者的線程中執(zhí)行。
也就是說,此連接類型,不但把信號(hào)發(fā)送到槽所在的線程事件中,而且會(huì)等待槽所在的線程事件處理完畢
,槽所在線程事件循環(huán)當(dāng)處理到此信號(hào)時(shí),才會(huì)執(zhí)行相應(yīng)操作,信號(hào)所在的線程才會(huì)繼續(xù)下一行代碼
UniqueConnection
資料太少,不知道此類型的大概用途。。。
這是一個(gè)標(biāo)志,可以結(jié)合上述任何一個(gè)連接類型,使用逐位或。當(dāng)Qt:UniqueConnection,QObject:connect()將會(huì)失敗如果連接已經(jīng)存在(即如果相同的信號(hào)已經(jīng)連接到同一個(gè)槽同一雙對(duì)象)。這個(gè)標(biāo)志是在Qt 4.6中引入的。