スレッド間の Qt シグナリング、1 つは GUI スレッドですか?
-
21-09-2019 - |
質問
Qt で moveToThread を使用してオブジェクトをあるスレッドから別のスレッドに移動するとはどういう意味ですか?moveToThread を使用する前でもすべてが機能しているように見えます。これは、オブジェクトをあるスレッド (GUI スレッド) から別のスレッドに移動し (機能しました)、Qt:connect がオブジェクトの適切なスロットを呼び出します。
オブジェクトが存在する場所、GUI スレッドまたはワーカー スレッドによって違いはありますか?
編集:小さなプログラムを作成しましたが、QThread と Signal およびスロット関数がどのように動作するのか理解できません。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を処理し、あなたは正しい接続を使用します。
編集:私は、スレッドが実際に作成された前に、彼はコンストラクタでスタートを呼んでいたので、記事の著者は彼の問題を見ていたと思います。言い換えれば、盲目的に、サードパーティのコードを信用していない。
編集:あなたのコメントを受けて、見マンデルブロの例、MandelbrotWidget Class Implementation
ヘッダ下
それは、後のスロットに渡すことができるようにはキューに入れられた接続では、Qtは信号に渡された引数のコピーを格納する必要があります。 Qtは、多くのC ++とQt種類のコピーを取る方法を知っている、しかし、QImageのはそのうちの一つではありません。私たちはキューに入れられた接続にパラメータとして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();
}
クリックボタン後の出力例:
Thread id 1088110320 Thread id inside run 1093176208 String address inside run 0x41288350 Thread id inside setText 1088110320 String address inside setText 0x974af58
あなたが見ることができるように、実行スレッドがメインのGUIスレッドとは異なります。それはスレッドの境界をコピー、それを横切るため、また、あなたは、QStringのへのconst参照を渡すにもかかわらず。 私の強くをお読みになることをお勧めスレッドとはQObject 。
他のヒント
の
QThread::start()
メソッドはスレッドを作成し、run()
実装。スレッド上でイベントまたは受信したシグナルを処理したい場合は、次のように呼び出す必要があります。QThread::exec()
内部 あなたのrun()
実装。絶対に電話してはいけませんrun()
明示的に呼び出す必要はありませんexec()
の外run()
.オーナー スレッドは、スロットが 以外の接続タイプの信号に接続されている場合にのみ違いを生じます。
Qt::DirectConnection
. 。その後、Qt はスロットが所有者スレッド上で実行されることを保証しますが、そのためには所有者スレッドが次のイベント ループを実行している必要があります。QThread::exec()
. 。この場合の呼び出しmyObj.moveToThread(myThread)
それを保証しますmyObj
スロットはスレッド上で実行されますmyThread
.スレッド オブジェクトは、スレッド オブジェクトが管理する (および 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();
}
新しいスレッドオブジェクト作成され、スレッドオブジェクトは、同じスレッドに移動します。スレッドと接続タイプの両方がキューであり、それが期待どおりに動作渡っシグナルは今です。
一部のオブジェクトは、所有者のみのスレッドで使用することができます。たとえば、あなたが作成し、ソケットオブジェクトを1つのスレッドで、あなたはそれが可能is'nt別のスレッドでデータを送信し、RECVしたい。場合したがって、一つの解決策は、他の1つのスレッドから、あなたのオブジェクトを移動し、その上で動作させることである。