昨晚在一個(gè)郵件列表里面看見一個(gè)關(guān)于在線程種使用signal/slot的討論,由于回復(fù)太多,這里就不貼出原文了。
主要是關(guān)于怎樣從一個(gè)線程發(fā)送信號(hào)到另外一個(gè)線程的問題。其實(shí)這個(gè)也不是什么復(fù)雜的問題,在qt的asstant里面已經(jīng)描訴的比較清楚了。當(dāng)我們鏈接信號(hào)的時(shí)候使用qt::queuedConnection就能使slot在它自己的線程里面運(yùn)行。
另我驚訝的是在其中一個(gè)的回復(fù)種他給出了一些資料,其中一個(gè)名為you‘ar doing it wrong。帖子是英文的,由于英文水平有限,加上他所說的使用QT thread的方式和我們平時(shí)直接派生QThread實(shí)現(xiàn)run函數(shù)的方式不一樣,所以讓我看的非常含糊,甚至到了不清不楚的地步。看了后面的大量的回復(fù)和討論,勉強(qiáng)明白了它的意思。
具體請(qǐng)看這里
http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/
在那里他提出了一種新的使用QThread的方式,其實(shí)也不算是信了,據(jù)說qt 4.4就已經(jīng)有了。那就是QObject::moveToThread。根據(jù)QT的asstant的描述,moveToThread的作用是把一個(gè)QOject移動(dòng)到一個(gè)線程里面去,那么它到底是什么意思呢。我的理解就是當(dāng)我們調(diào)用QObject的moveToThread方法之后,我們這個(gè)派生自QObject的類的代碼就會(huì)在新的線程里面執(zhí)行。而那篇文章所說的就是大多數(shù)對(duì)這個(gè)函數(shù)產(chǎn)生了誤解,人們總是在派生的QThread的類的構(gòu)造函數(shù)里面調(diào)用moveToThread(this)以希望把該類的所有函數(shù)都在該線程里面執(zhí)行。這樣是錯(cuò)誤的。
今天為了驗(yàn)證這個(gè)方法到底有什么用,寫了一些代碼來做測(cè)試。
1、
- #include <QObject>
- #include <QDebug>
- #include <QThread>
-
- class MyObject : public QObject {
- Q_OBJECT
- public:
- MyObject() {};
- ~MyObject() {}
-
- public slots:
- void first() {
- qDebug() << QThread::currentThreadId();
- }
- void second() {
- qDebug() << QThread::currentThreadId();
- }
- void three() {
- qDebug() << QThread::currentThreadId();
- }
- };
2、mainwindow.cxx
- #include "mainwindow.h"
- #include <QPushButton>
- #include <QVBoxLayout>
- #include "myobject.h"
-
- MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
- my = new MyObject;
- firstButton = new QPushButton(tr("first"), 0);
- connect(firstButton, SIGNAL(clicked()), my, SLOT(first()), Qt::QueuedConnection);
- secondButton = new QPushButton(tr("second"), 0);
- connect(secondButton, SIGNAL(clicked()), my, SLOT(second()), Qt::QueuedConnection);
- threeButton = new QPushButton(tr("three"), 0);
- connect(threeButton, SIGNAL(clicked()), my, SLOT(three()), Qt::QueuedConnection);
- selfButton = new QPushButton(tr("self"), 0);
- connect(selfButton, SIGNAL(clicked()), this, SLOT(onSelfPushed()));
- exitButton = new QPushButton(tr("exit"), 0);
- connect(exitButton, SIGNAL(clicked()), this, SLOT(onExitPushed()));
-
- QVBoxLayout *layout = new QVBoxLayout;
- layout->addWidget(firstButton);
- layout->addWidget(secondButton);
- layout->addWidget(threeButton);
- layout->addWidget(selfButton);
- layout->addWidget(exitButton);
-
- QWidget *p = new QWidget;
- p->setLayout(layout);
-
- QThread *thread = new QThread;
- my->moveToThread(thread);
-
- thread->start();
- connect(thread, SIGNAL(started()), my, SLOT(first()));
-
- setCentralWidget(p);
- }
-
- MainWindow::~MainWindow() {
- }
-
- void MainWindow::onFirstPushed() {
- my->first();
- }
-
- void MainWindow::onSecondPushed() {
- my->second();
- }
-
- void MainWindow::onThreePushed() {
- my->three();
- }
-
- void MainWindow::onSelfPushed() {
- qDebug() << QThread::currentThreadId();
- }
-
- void MainWindow::onExitPushed() {
- close();
- }
通過測(cè)試,在mainwidow.cxx使用上面的代碼的時(shí)候,由于my調(diào)用了movetothread函數(shù),那么它所有的槽函數(shù)都是執(zhí)行在新開辟的線程里面。 如果去掉moveToThread函數(shù),那么所有的函數(shù)都將執(zhí)行在gui線程里面。
同時(shí)為了測(cè)試connect的第五個(gè)參數(shù),在connect的時(shí)候可以將Qt::QueuedConnection修改為Qt::DirectConnection,這樣所有的槽函數(shù)也將在主線程里面執(zhí)行。
最后要注意的是,如果上面connect的時(shí)候連接的是this的onXXXXXX槽函數(shù)再來調(diào)用的my的槽函數(shù)的話,那么這些槽函數(shù)也將執(zhí)行在onXXXXX槽函數(shù)所在的線程,這里是主線程。
通過上面的測(cè)試,我們?cè)谑褂镁€程的時(shí)候,就可以將一個(gè)類派生自QObject,然后實(shí)現(xiàn)所有的signal/slot,然后通過調(diào)用movetothread函數(shù)來使他們執(zhí)行在新的線程里面,而不是每次都要重新派生QThread,并且派生QThread函數(shù)的另外一個(gè)不好的地方是只有run函數(shù)內(nèi)部的代碼才會(huì)執(zhí)行在新線程里面,相比起來,派生QObject并使用movetothread函數(shù)更具有靈活性。
最后,把討論中列出的所有的網(wǎng)址列出來哈。