web-dev-qa-db-ja.com

Qtでクロススレッド信号を発信する方法は?

Qtのドキュメントでは、シグナルとスロットはdirectqueuedautoにできると述べています。

また、スロットを所有するオブジェクトが信号を所有するオブジェクトとは異なるスレッドで「生きている」場合、そのような信号を発信することはメッセージを投稿するようなものであり、信号発信は即座に戻り、スロットメソッドはターゲットスレッドのイベントループで呼び出されます。

残念ながら、ドキュメンテーションでは、「ライブ」が表すことを指定しておらず、使用可能な例はありません。私は次のコードを試しました:

_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()が呼び出されることはありません:(。何が間違っていますか?

58
grigoryvp

コードにはかなりの問題があります:

  • エヴァンが言ったように、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で呼び出されます。

46
Aiua

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を使用している場合、ほぼ確実にnotサブクラスQThreadを使用する必要があります。

MoveToThreadを使用する

ワーカースレッドで実行するスロットを取得するための鍵は、Aiuaが指摘したようにmoveToThreadメソッドを使用することです。

36
Will Bickford

次のようなスレッド関数を開始するためのシグナルを発行する必要があります

emit operateCut(examId,examName_examTemplate[examName].studentIdRec,examName_examTemplate[examName].choiceRecA,examName_examTemplate[examName].choiceRecB,examName_examTemplate[examName].objectRecA,examName_examTemplate[examName].objectRecB);

このシグナルに複数の引数を追加できます

0
kuake56