Qt moveToThread

英文原文:http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/

我们之间经常使用IRC(网络聊天工具?)进行交流,社区也同样是这样。我在上 Freenode 网络的 QT 频道时会帮那些有问题的人解答。一个普遍的问题(也是我厌烦的问题)是如何理解QT多线程,还有如何使他们的代码工作。他们将他们的代码,或者基于他们代码的实例展示给我看,而我经常会回复他们一句:

你做错了。。。

我知道这个回答是有些无礼的,甚至是有些讽刺的。但我不禁想到,下面的这个类(假想)是一个不正确的多线程原则的应用,也是不正确的QT运用。

class MyThread : public QThread
{
public:
    MyThread()
    {
        moveToThread(this);
    }
    void run();
signals:
    void progress(int);
    void dataReady(QByteArray);
public slots:
    void doWork();
    void timeoutHandler();
};

我最不能忍受的代码是moveToThread(this); 我看到非常多的人在不理解这句话做了什么的情况下使用它。它做了什么?moveToThread函数让QT确保事件句柄和扩展的信号和槽已经被指定的线程上下文调用,因为QThread是一个线程接口,所以我们让线程的代码运行在“它自己里面”,我们也会在线程运行之前做这样的事。即使这样似乎能工作,但它令人迷惑,而且这也不是QThread设计的使用方式(所有QThread的函数编写时是希望在线程创建时被调用,而不是QThread类初始化时)。 我想,moveToThread(this);如此“恐怖的” (creeps) 写入到人们的代码中是因为他们看到有些博客也这么用。简单的网络搜索就会出现好几篇这样的博客,大家基本都是按照下面的方法做的:

  1. 继承QThread
  2. 添加一些消息和槽来完成工作
  3. 测试代码,发现那些槽并不是从“正确的线程”中调用的
  4. 问Google,找到moveToThread(this);方法,然后加了些注释:“当我添加这个的时候它似乎工作得很好

在我看来,问题出在第一步。QThread 是被设计来作为接口或者操作系统线程控制点(a control point to an operating system thread)使用的,不是一个放置你的多线程代码的地方。我们面向对象程序员继承一个类是为了扩展或者特殊化(specialize )基类的方法。我想,继承QThread的唯一正当的理由是添加QThread没有的方法,例如,也许要提供一个内存指针来实现线程栈,或者可能是添加实时接口支持。一段下载文件,查询数据库,或者任何其他处理的代码不应该继承QThread。它应该独立(encapsulated)在它自己的对象中。

通常,这意味着简单的从QThread修改你的代码到QObject,然后,可能需要修改你的类名。当你需要做些初始化时,你可以连接QThread的started信号。你需要实例化一个QThread并使用moveToThread(this);函数指派你的对象到此线程中,让你的代码真正运行在新的线程上下文中。即使你仍然是使用moveToThread函数让QT在指定的线程上下文中运行你的代码,但是我们保持了线程接口的独立性。如果有必要,现在就可能是时候将你的类的多个实例指派到一个单独的线程中,或者多个类的多个实例指派到一个单独的线程中了,换句话说,每个线程绑定一个实例也许是不必要的。(可以减少不必要的线程创建,译者注)

我常常埋怨那些混淆的线程相关的QT代码的编写。原始的QThread类是抽象的,所以继承是必要的。直到QT4.4,QThread::run()实现了一个默认的定义。在这之前,使用QThread唯一的方法是继承它。但是随着线程相关的添加和不同类别的对象之间的信号和槽的连接,我们突然有了一个方便的线程使用方法。我们喜欢简单,所以我们希望使用它。但不幸的是已经晚了。迫使人们继承QThread已经使它变得比预想的更难以改变。 我也埋怨没有一份最新的示例代码或者文档来告诉人们如何理由便捷的方法使他们从头疼中解脱出来。到现在为止,我发现的最好的资源是一篇我以前写的博客

免责声明:上面你看到的所有内容是显而易见的观点。我做了很多关于这些类的工作,我很清楚的知道如何来使用或者如何不使用它们。


使用QThread线程的新方法QObject::moveToThread 
昨晚在一个邮件列表里面看见一个关于在线程种使用signal/slot的讨论,由于回复太多,这里就不贴出原文了。

        主要是关于怎样从一个线程发送信号到另外一个线程的问题。其实这个也不是什么复杂的问题,在qt的asstant里面已经描诉的比较清楚了。当我们链接信号的时候使用qt::queuedConnection就能使slot在它自己的线程里面运行。

        另我惊讶的是在其中一个的回复种他给出了一些资料,其中一个名为you‘ar doing it wrong。帖子是英文的,由于英文水平有限,加上他所说的使用QT thread的方式和我们平时直接派生QThread实现run函数的方式不一样,所以让我看的非常含糊,甚至到了不清不楚的地步。看了后面的大量的回复 和讨论,勉强明白了它的意思。

    具体请看这里[喝小酒的网摘]http://blog.const.net.cn/a/9900.htm
http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/

       在那里他提出了一种新的使用QThread的方式,其实也不算是信了,据说qt 4.4就已经有了。那就是QObject::moveToThread。根据QT的asstant的描述,moveToThread的作用是把一个 QOject移动到一个线程里面去,那么它到底是什么意思呢。我的理解就是当我们调用QObject的moveToThread方法之后,我们这个派生自 QObject的类的代码就会在新的线程里面执行。而那篇文章所说的就是大多数对这个函数产生了误解,人们总是在派生的QThread的类的构造函数里面 调用moveToThread(this)以希望把该类的所有函数都在该线程里面执行。这样是错误的。

         今天为了验证这个方法到底有什么用,写了一些代码来做测试。

1、

   

  1. #include <QObject>  
  2. #include <QDebug>  
  3. #include <QThread>  
  4.   
  5. class MyObject : public QObject {  
  6.     Q_OBJECT  
  7.     public:  
  8.         MyObject() {};  
  9.         ~MyObject() {}  
  10.   
  11.     public slots:  
  12.         void first() {  
  13.             qDebug() << QThread::currentThreadId();  
  14.         }  
  15.         void second() {  
  16.             qDebug() << QThread::currentThreadId();  
  17.         }  
  18.         void three() {  
  19.             qDebug() << QThread::currentThreadId();  
  20.         }  
  21. };  

2、mainwindow.cxx

  1. #include "mainwindow.h"  
  2. #include <QPushButton>  
  3. #include <QVBoxLayout>  
  4. #include "myobject.h"  
  5.   
  6. MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {  
  7.     my = new MyObject;  
  8.     firstButton = new QPushButton(tr("first"), 0);  
  9.     connect(firstButton, SIGNAL(clicked()), my, SLOT(first()), Qt::QueuedConnection);  
  10.     secondButton = new QPushButton(tr("second"), 0);  
  11.     connect(secondButton, SIGNAL(clicked()), my, SLOT(second()), Qt::QueuedConnection);  
  12.     threeButton = new QPushButton(tr("three"), 0);  
  13.     connect(threeButton, SIGNAL(clicked()), my, SLOT(three()), Qt::QueuedConnection);  
  14.     selfButton = new QPushButton(tr("self"), 0);  
  15.     connect(selfButton, SIGNAL(clicked()), this, SLOT(onSelfPushed()));  
  16.     exitButton = new QPushButton(tr("exit"), 0);  
  17.     connect(exitButton, SIGNAL(clicked()), this, SLOT(onExitPushed()));  
  18.   
  19.     QVBoxLayout *layout = new QVBoxLayout;  
  20.     layout->addWidget(firstButton);  
  21.     layout->addWidget(secondButton);  
  22.     layout->addWidget(threeButton);  
  23.     layout->addWidget(selfButton);  
  24.     layout->addWidget(exitButton);  
  25.   
  26.     QWidget *p = new QWidget;  
  27.     p->setLayout(layout);  
  28.   
  29.     QThread *thread = new QThread;  
  30.     my->moveToThread(thread);  
  31.   
  32.     thread->start();  
  33.     connect(thread, SIGNAL(started()), my, SLOT(first()));  
  34.   
  35.     setCentralWidget(p);  
  36. }  
  37.   
  38. MainWindow::~MainWindow() {  
  39. }  
  40.   
  41. void MainWindow::onFirstPushed() {  
  42.     my->first();  
  43. }  
  44.   
  45. void MainWindow::onSecondPushed() {  
  46.     my->second();  
  47. }  
  48.   
  49. void MainWindow::onThreePushed() {  
  50.     my->three();  
  51. }  
  52.   
  53. void MainWindow::onSelfPushed() {  
  54.     qDebug() << QThread::currentThreadId();  
  55. }  
  56.   
  57. void MainWindow::onExitPushed() {  
  58.     close();  
  59. }  

    通过测试,在mainwidow.cxx使用上面的代码的时候,由于my调用了movetothread函数,那么它所有的槽函数都是执行在新开辟的线程里面。

   如果去掉moveToThread函数,那么所有的函数都将执行在gui线程里面。

   同时为了测试connect的第五个参数,在connect的时候可以将Qt::QueuedConnection修改为Qt::DirectConnection,这样所有的槽函数也将在主线程里面执行。

     最后要注意的是,如果上面connect的时候连接的是this的onXXXXXX槽函数再来调用的my的槽函数的话,那么这些槽函数也将执行在onXXXXX槽函数所在的线程,这里是主线程。

     通过上面的测试,我们在使用线程的时候,就可以将一个类派生自QObject,然后实现所有的signal/slot,然后通过调用 movetothread函数来使他们执行在新的线程里面,而不是每次都要重新派生QThread,并且派生QThread函数的另外一个不好的地方是只 有run函数内部的代码才会执行在新线程里面,相比起来,派生QObject并使用movetothread函数更具有灵活性。

    最后,把讨论中列出的所有的网址列出来哈。

http://qt-project.org/doc/qt-4.8/thread-basics.html
http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/
http://qt-project.org/wiki/Threads_Events_QObjects
http://qt-project.org/wiki/QThreads_general_usage
http://labs.qt.nokia.com/2006/12/04/threading-without-the-headache/
原文:http://blog.csdn.net/sydnash/article/details/7425947[喝小酒的网摘]http://blog.const.net.cn/a/9900.htm
展开阅读全文

没有更多推荐了,返回首页