在 Qt 中使用 moveToThread 将对象从一个线程移动到另一个线程意味着什么?即使在使用 moveToThread 之前,一切似乎都可以正常工作,它将对象从一个线程(GUI 线程)移动到另一个线程(工作),并且 Qt:connect 调用对象上适当的槽。

对象所在的位置(GUI 线程还是工作线程)有什么不同吗?

编辑:我编写了一个小程序,但我不明白 QThread 如何与 Signal 和 slot 函数一起工作,如果您能用示例解释 moveToThread 的用途,我将不胜感激

#include <QtGui/QApplication>
#include <QPushButton>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QString>
#include "mythread.h"
//GUI calls a thread to do some job and sub update the text box once it is done
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    QHBoxLayout * pH = new QHBoxLayout(&w);
    QPushButton * pushButton = new QPushButton("asdad");
    QLineEdit * lineEdit = new QLineEdit("AAA");
    pH->addWidget(pushButton);
    pH->addWidget(lineEdit);
    w.setLayout(pH);
    w.show();
    MyThread thread;
    qDebug("Thread id %d",(int)QThread::currentThreadId());
    QObject::connect(pushButton,SIGNAL(clicked()),&thread,SLOT(callRun())) ;
    QObject::connect(&thread,SIGNAL(signalGUI(QString)),lineEdit,SLOT(setText(QString)));
    return a.exec();
}

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QMutex>

class MyThread : public QThread
{
    Q_OBJECT
public:
    MyThread();
public slots:
    void callRun();
    void run();
 signals:
    void signalGUI(QString);
private:
    QMutex mutex;

};

#endif // MYTHREAD_H


#include "mythread.h"
#include <QDebug>
#include <QString>
#include <QMutexLocker>

MyThread::MyThread()
{
}
 void MyThread::callRun()
 {

     qDebug("in thread");
    if(!isRunning())
     {
        this->start(LowestPriority);
        exec();
    }
    else
    {
        run();
    }

 }
 void MyThread::run()
 {
     QMutexLocker fn_scope(&mutex);
     static int a = 0;
    ++a;
     qDebug("Thread id inside run %d",(int)QThread::currentThreadId());
     this->sleep(3);
     static QString number;
     QString temp;
     number += temp.setNum(a);
     emit signalGUI(number);
 }
有帮助吗?

解决方案

看一看信号和跨线程槽的。如果你总是使用信号和槽与工人线程进行通信,Qt的处理moveToThread你,如果它需要你使用正确的连接。

编辑:我猜文章的作者是看到他的问题,因为他是在构造函数中调用start实际创建线程之前。换句话说,不盲目信任第三方代码。

编辑:在回答你的评论,看看曼德尔布罗例如,MandelbrotWidget Class Implementation标题下:

  

使用排队连接,Qt的必须存储的被传递给信号,即自变量的副本,以便它可以将它们传递到槽以后。 Qt的知道如何利用许多C ++和Qt类型的副本,但QImage的是不是其中之一。因此,我们必须调用模板函数qRegisterMetaType()之前,我们可以在排队的连接使用QImage的作为参数。

我相信这是略显陈旧,这里有有效元类型。由于跨线程的信号和槽使用排队的连接,你不应该做moveToThread在大多数情况下调用。

编辑: 我会尝试使用具有类似的例子来解释的东西:

mythread.h:

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QMutex>

class MyThread : public QThread
{
   Q_OBJECT

protected:
   virtual void run();

signals:
   void signalGUI(QString);
};

#endif // MYTHREAD_H

mythread.cpp:

#include "mythread.h"
#include <QString>

void MyThread::run()
{
   qDebug("Thread id inside run %d",(int)QThread::currentThreadId());
   static int run = 0;
   QString temp = QString("Run: %1").arg(run++);
   qDebug("String address inside run %p", &temp);
   emit signalGUI(temp);
}

mylineedit.h

#ifndef MYLINEEDIT_H
#define MYLINEEDIT_H

#include <QLineEdit>

class MyLineEdit : public QLineEdit
{
Q_OBJECT
public:
    explicit MyLineEdit(QWidget *parent = 0);

public slots:
    void setText(const QString &string);

};

#endif // MYLINEEDIT_H

mylineedit.cpp

#include "mylineedit.h"
#include <QThread>

MyLineEdit::MyLineEdit(QWidget *parent) :
    QLineEdit(parent)
{
}

void MyLineEdit::setText(const QString &string)
{
   qDebug("Thread id inside setText %d",(int)QThread::currentThreadId());
   qDebug("String address inside setText %p\n", &string);
   QLineEdit::setText(string);
}

main.cpp中:

#include <QApplication>
#include <QPushButton>
#include <QHBoxLayout>
#include "mythread.h"
#include "mylineedit.h"

//GUI calls a thread to do some job and sub update the text box once it is done
int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   QWidget w;
   QHBoxLayout * pH = new QHBoxLayout(&w);
   QPushButton * pushButton = new QPushButton("Run Thread", &w);
   MyLineEdit * lineEdit = new MyLineEdit(&w);

   pH->addWidget(pushButton);
   pH->addWidget(lineEdit);
   w.show();

   MyThread thread;
   qDebug("Thread id %d",(int)QThread::currentThreadId());
   QObject::connect(pushButton,SIGNAL(clicked()),&thread,SLOT(start())) ;
   QObject::connect(&thread,SIGNAL(signalGUI(const QString&)),lineEdit,SLOT(setText(const QString&)));
   return a.exec();
}

示例输出点击按钮后:

Thread id 1088110320
Thread id inside run 1093176208
String address inside run 0x41288350
Thread id inside setText 1088110320
String address inside setText 0x974af58

可以看到,运行线程比主GUI线程不同。而且,即使你传递一个const引用为QString,因为它跨越线程边界它复制它。 我强烈建议您阅读线程和QObject的

其他提示

  1. QThread::start() 方法创建线程并调用您的 run() 执行。如果你想在线程上处理事件或接收信号,你必须调用 QThread::exec() 里面 你的 run() 执行。你永远不应该打电话 run() 明确地,你不应该打电话 exec() 在外面 run().

  2. 仅当插槽连接到连接类型不同于以下类型的信号时,所有者线程才会产生影响: Qt::DirectConnection. 。然后 Qt 将确保槽在所有者线程上运行,但为此所有者线程必须运行事件循环 QThread::exec(). 。在这种情况下调用 myObj.moveToThread(myThread) 将确保 myObj 槽在线程上运行 myThread.

  3. 线程对象属于创建它的线程,而不是它管理的线程(以及 run 方法将运行的线程)。因此,当您将信号连接到线程对象的插槽时,该插槽将在创建线程对象的线程中运行,除非您调用 moveToThread().

当移动线程之间的对象,则决定它属于哪个事件循环。当一个线程内进行连接,所述信令代码直接调用时隙中的每一个(不必等待他们完成)。跨线程边界信令放置在事件循环的信号呼叫,让槽的线程进行调用到插槽中时,准备好了。

制作线程之间的直接调用需要你确保你的函数是重入的。你还必须确保以保护使用互斥量或信号数据,并同时避免竞争状态。

在本文中,我想,延迟是由于呼叫被直接,即不都在后台处理(但我只脱脂的文本)。

#include <QtGui/QApplication>
#include <QPushButton>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QString>
#include "mythread.h"
//GUI calls a thread to do some job and sub update the text box once it is done
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    QHBoxLayout * pH = new QHBoxLayout(&w);
    QPushButton * pushButton = new QPushButton("asdad");
    QLineEdit * lineEdit = new QLineEdit("AAA");
    pH->addWidget(pushButton);
    pH->addWidget(lineEdit);
    w.setLayout(pH);
    w.show();
    MyThread thread;
    thread.moveToThread(&thread);
    thread.start();
    qDebug("Thread id %d",(int)QThread::currentThreadId());
    QObject::connect(pushButton,SIGNAL(clicked()),&thread,SLOT(callRun()),Qt::QueuedConnection) ;
    QObject::connect(&thread,SIGNAL(signalGUI(QString)),lineEdit,SLOT(setText(QString)),Qt::DirectConnection);
    return a.exec();
}

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QMutex>

class MyThread : public QThread
{
    Q_OBJECT
public:
    MyThread();
public slots:
    void callRun();
    void run();
 signals:
    void signalGUI(QString);
private:
    QMutex mutex;

};

#endif // MYTHREAD_H
#include "mythread.h"
#include <QDebug>
#include <QString>
#include <QMutexLocker>

MyThread::MyThread()
{
}
 void MyThread::callRun()
 {
     QMutexLocker fn_scope(&mutex);
     static int a = 0;
    ++a;
     qDebug("Thread id inside run %d",(int)QThread::currentThreadId());
     this->sleep(3);
     static QString number;
     QString temp;
     number += temp.setNum(a);
     emit signalGUI(number);

 }
 void MyThread::run()
 {
    exec();
 }

时,创建的新对象的线程和线程对象被移动到在同一线程。信号现在跨线程和连接类型均队列,它按预期工作。

某些对象只能所有者线程上使用。例如,如果您创建并在一个线程Socket对象,你要在它is'nt可能另一个线程发送和recv数据。因此一种解决方案是从一个线程移动您的目的是其他并在其上进行操作。

scroll top