29

moveToThreadを使用してQtでオブジェクトをあるスレッドから別のスレッドに移動するとはどういう意味ですか? オブジェクトをあるスレッド (GUI スレッド) から別のスレッド (動作) に移動し、Qt:connect がオブジェクトの適切なスロットを呼び出す moveToThread を使用する前でも、すべてが機能しているようです。

オブジェクトが存在する場所、GUI スレッドまたはワーカー スレッドによる違いはありますか?

編集:小さなプログラムを作成しましたが、QThread がシグナルとスロット関数と共にどのように機能するかわかりません。例で 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);
 }
4

5 に答える 5

38

Signals and slot across threads を見てください。常にシグナルとスロットを使用してワーカー スレッドと通信する場合、Qt は必要に応じて moveToThread を処理し、正しい接続を使用します。

編集: スレッドが実際に作成される前にコンストラクターで start を呼び出していたため、記事の作成者は問題を認識していたと思います。つまり、サードパーティのコードを盲目的に信用しないでください。

編集:あなたのコメントに応えて、ヘッダーの下のマンデルブロの例を見てください:MandelbrotWidget Class Implementation

キュー接続では、Qt はシグナルに渡された引数のコピーを保存して、後でスロットに渡すことができるようにする必要があります。Qt は多くの C++ および Qt 型のコピーを取る方法を知っていますが、QImage はそれらの 1 つではありません。したがって、QImage をキュー接続のパラメーターとして使用する前に、テンプレート関数 qRegisterMetaType() を呼び出す必要があります。

これは少し古いと思います。有効なメタ タイプは次のとおりです。スレッド間のシグナルとスロットはキュー接続を使用するため、ほとんどの場合、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();
}

ボタンをクリックした後の出力例:

スレッドID 1088110320
実行内のスレッド ID 1093176208
run 0x41288350 内の文字列アドレス
setText 1088110320 内のスレッド ID
setText 0x974af58 内の文字列アドレス

ご覧のとおり、実行スレッドはメインの GUI スレッドとは異なります。また、QString に const 参照を渡しても、それはスレッドの境界を越えるため、それをコピーします。Threads と QObjectを読むことを強くお勧めします。

于 2010-01-18T17:47:44.167 に答える
8
  1. QThread::start()メソッドはスレッドを作成し、実装を呼び出しますrun()。スレッドでイベントまたは受信したシグナルを処理する場合は、実装QThread::exec() run()で呼び出す必要があります。run()明示的に呼び出したり、外部で呼び出したりしないでexec()くださいrun()

  2. 所有者スレッドは、スロットが 以外の接続タイプの信号に接続されている場合にのみ違いを生みますQt::DirectConnection。次に、Qt はスロットがオーナー スレッドで実行されることを保証しますが、そのためにはオーナー スレッドが でイベント ループを実行している必要がありますQThread::exec()。この場合、呼び出しにより、スロットがスレッド上で実行さmyObj.moveToThread(myThread)れることが保証されます。myObjmyThread

  3. スレッド オブジェクトは、それが管理するスレッド (および run メソッドが実行される場所) ではなく、それが作成されたスレッドに属します。そのため、シグナルをスレッド オブジェクトのスロットに接続すると、 を呼び出さない限り、スレッド オブジェクトが作成されたスレッドでそのスロットが実行されますmoveToThread()

于 2010-01-22T10:40:26.083 に答える
3

スレッド間でオブジェクトを移動するときは、オブジェクトが属するイベントループを決定します。スレッド内で接続を確立する場合、シグナリングコードは各スロットを直接呼び出します(スロットが終了するのを待つ必要があります)。スレッドの境界を越えてシグナリングすると、シグナル呼び出しがイベントループに配置され、準備ができたらスロットのスレッドがスロットを呼び出すことができます。

スレッド間で直接呼び出しを行うには、関数が再入可能であることを確認する必要があります。また、ミューテックスまたはセマフォを使用してデータを保護すると同時に、競合状態を回避する必要があります。

この記事では、遅延は呼び出しが直接行われているため、つまりバックグラウンドでまったく処理されていないためだと思います(ただし、テキストをスキミングしただけです)。

于 2010-01-18T14:45:07.373 に答える
3
#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();
 }

新しいスレッド オブジェクトが作成され、スレッド オブジェクトが同じスレッドに移動されます。シグナルはスレッドをまたいでおり、接続タイプは両方ともキューであり、期待どおりに機能します。

于 2010-01-20T14:46:54.770 に答える
0

一部のオブジェクトは所有者スレッドでのみ使用できます。たとえば、あるスレッドでオブジェクトを作成してソケットに入れ、別のスレッドでデータを送受信したい場合、それは不可能です。したがって、1 つの解決策は、オブジェクトをあるスレッドから別のスレッドに移動して操作することです。

于 2010-01-19T17:10:57.997 に答える