Qtでスレッド間シグナルを発信する方法は?
-
10-07-2019 - |
質問
Qtのドキュメントには、シグナルとスロットを direct
、 queued
、および auto
にできることが記載されています。
また、スロットを所有するオブジェクトが信号を所有するオブジェクトとは異なるスレッドで「生きている」場合、そのような信号を発信することはメッセージを投稿するようなものであり、信号発信は即座に戻り、ターゲットスレッドのイベントループでスロットメソッドが呼び出されます。
残念ながら、ドキュメンテーションでは「lives」が表すことを指定しておらず、使用可能な例はありません。次のコードを試しました:
main.h:
class CThread1 : public QThread
{
Q_OBJECT
public:
void run( void )
{
msleep( 200 );
std::cout << "thread 1 started" << std::endl;
MySignal();
exec();
}
signals:
void MySignal( void );
};
class CThread2 : public QThread
{
Q_OBJECT
public:
void run( void )
{
std::cout << "thread 2 started" << std::endl;
exec();
}
public slots:
void MySlot( void )
{
std::cout << "slot called" << std::endl;
}
};
main.cpp:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
CThread1 oThread1;
CThread2 oThread2;
QObject::connect( & oThread1, SIGNAL( MySignal() ),
& oThread2, SLOT( MySlot() ) );
oThread1.start();
oThread2.start();
oThread1.wait();
oThread2.wait();
return a.exec();
}
出力:
thread 2 started
thread 1 started
MySlot()
が呼び出されることはありません:(。何が間違っていますか?
解決
コードにはかなりの問題があります:
- evanによると、emitキーワードが欠落していると言われています
- すべてのオブジェクトはメインスレッドに存在し、runメソッドのコードのみが他のスレッドに存在します。つまり、MySlotスロットがメインスレッドで呼び出され、それがあなたが望むものかわかりません
- メインイベントループが起動されないため、スロットは呼び出されません。wait()への2回の呼び出しは、非常に長い時間後にのみタイムアウトします(そして、おそらくそれが起こる前にアプリケーションを強制終了します)。とにかく彼らはあなたのコードで役に立たないのです。
このコードはおそらく動作するでしょう(私はテストしていませんが)、あなたがやりたいことをやってくれると思います:
class MyObject : public QObject
{
Q_OBJECT
public slots:
void MySlot( void )
{
std::cout << "slot called" << std::endl;
}
};
class CThread1 : public QThread
{
Q_OBJECT
public:
void run( void )
{
std::cout << "thread 1 started" << std::endl;
int i = 0;
while(1)
{
msleep( 200 );
i++;
if(i==1000)
emit MySignal();
}
}
signals:
void MySignal( void );
};
class CThread2 : public QThread
{
Q_OBJECT
public:
void run( void )
{
std::cout << "thread 2 started" << std::endl;
exec();
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
CThread1 oThread1;
CThread2 oThread2;
MyObject myObject;
QObject::connect( & oThread1, SIGNAL( MySignal() ),
& myObject, SLOT( MySlot() ) );
oThread2.start();
myObject.moveToThread(&oThread2)
oThread1.start();
return a.exec();
}
MyObjectはthread2に残ります(moveToThreadに感謝します)。
MySignalはthread1から送信される必要があります(その1つについては定かではありませんが、メインスレッドから送信される可能性がありますが、実際には問題ではありません)。
信号の発信にはイベントループが必要ないため、thread1ではイベントループは必要ありません。シグナルを受信するには、thread2(exec()によって待機)でイベントループが必要です。
MySlotはthread2で呼び出されます。
他のヒント
Qt 4.4+のQThreadをサブクラス化しない
Aiuaの答えは良いですが、QThreadとQt 4.6または4.7のいくつかの問題を指摘したいと思います。
この記事の要約: http:/ /blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/
Qtのパートに関するドキュメントの欠如
残念なことに、問題はドキュメントの更新がないことに起因しています。 Qt 4.4より前は、QThreadにはデフォルトのrun()実装がありませんでした。これを使用するには、QThreadをサブクラス化する必要がありました。
Qt 4.6または4.7を使用している場合、ほぼ確実にQThreadをサブクラス化するべきではありません 。
moveToThreadを使用
ワーカースレッドでスロットを実行するための鍵は、Aiuaが指摘したようにmoveToThreadメソッドを使用することです。
次のようにスレッド関数を開始する信号を送信する必要があります
emit operateCut(examId,examName_examTemplate[examName].studentIdRec,examName_examTemplate[examName].choiceRecA,examName_examTemplate[examName].choiceRecB,examName_examTemplate[examName].objectRecA,examName_examTemplate[examName].objectRecB);
このシグナルに複数の引数を追加できます