4

マルチカメラ システムを別のカメラを処理する別のスレッドで動作させようとしているときに、異なるスレッド間でシグナルとスロットが正しく動作することができませんでした。シグナルを送信するオブジェクトと関連するスロットのオブジェクトが別のスレッドにあるという事実に何か問題があることはわかっていたので、おそらく接続に適切な「接続タイプ」パラメータを見つけるだけでよいことがわかっていました。最終的に、Qt::DirectConnection を使用するだけですべてが正常に機能することがわかりました。

以下の簡略化されたコードを見つけてください。ここでは、すべてがどのように機能するかについて簡単に説明します。

  • アプリケーションは、すべてのスレッドを作成して開始するメインプログラムです。この単純化されたバージョンでは、ワーカーがスロット「quit」を介してジョブを完了するのを待つだけです。

  • Worker は、スレッド化されたタスクの 1 つを実行するオブジェクトです。この単純化された例では、計算が完了するまでしばらく待ちます。次に、ワーカーはアプリケーション インスタンスに向けられたシグナルを送信します。これにより、アプリケーション インスタンスはすべてのスレッドを待機し、QCoreApplication を終了することができます。

私が見つけたのは、2番目の接続で Qt::DirectConnection を使用しない場合、ワーカーの finished() シグナルがスレッドの quit() スロットをトリガーしないことです。つまり、アプリケーションはハングしたままになりますスレッドを待っています。

私の質問は次のとおりです。なぜそうなのですか?2 つのオブジェクト (ワーカーとスレッド) は異なるスレッドに属しているため、QueuedConnection などを使用するべきではありませんか? DirectConnection は、同じスレッドに属するオブジェクトにのみ使用する必要があると考えました。

main.cpp:

#include <iostream>
#include <QCoreApplication>
#include "program.h"

using namespace std;

int main(int argc, char **argv) {
  QCoreApplication app(argc, argv);

  Program *p = new Program;
  p->execute();

  app.exec();

  delete p;
}

プログラム.h

#ifndef _PROGRAM_H_
#define _PROGRAM_H_

#include <QThread>
#include <QTimer>
#include "worker.h"

class Program: public QObject {
  Q_OBJECT

  private:
    Worker *worker;
    QThread *thread;

  public:
    void execute();

  public slots:
    void quit();
};

#endif // _PROGRAM_H_

プログラム.cpp

#include "worker.h"

using namespace std;

void Program::execute() {
  worker = new Worker();
  thread = new QThread;
  worker->moveToThread(thread);

  cout << "Connection established: "
        << connect(thread, SIGNAL(started()), worker, SLOT(process()))
        << endl;

  // slot not called if I remove the fifth parameter
  // or if I put Qt::QueuedConnection
  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), thread, SLOT(quit()),
                    Qt::DirectConnection)
        << endl;

  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), this, SLOT(quit()))
        << endl;

  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()))
        << endl;

  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), thread, SLOT(deleteLater()))
        << endl;

  thread->start();
}

void Program::quit() {
  cout << "waiting.." << endl;
  thread->wait();
  cout << "           .. I'm done!" << endl;

  cout << "quitting from all.." << endl;
  QCoreApplication::quit();
  cout << "           .. I'm done!" << endl;
}

#include "program_moc.cpp"

worker.h

#ifndef _WORKER_H_
#define _WORKER_H_

#include <QObject>

class Worker: public QObject {
  Q_OBJECT

  public slots:
    void process();

  signals:
    void finished();
};

#endif // _WORKER_H_

ワーカー.cpp:

#include <iostream>
#include <unistd.h>
#include "worker.h"

using namespace std;

void Worker::process() {
  cout << "Worker::process() started" << endl;
  usleep(1000000);

  cout << "Worker::finished() being emitted" << endl;
  emit finished();

  cout << "Worker::process() finished" << endl;
}

#include "worker_moc.cpp"

編集

@ariwez の回答に従うと、この特定の単純化された例では問題が解決されますが、少し複雑な例では解決されません。これを今追加しています。

この例では、

  • プログラムには、QTimer を使用して定期的に実行する独自のジョブがあります。プログラムには、ユーザーがプログラムを終了することをシミュレートするために使用されるさらに別の QTimer もあり、スロット Program::quit() の実行をトリガーします。

  • ワーカーは、終了フラグが false に設定されるまで、独自のジョブを実行します。これは Program::quit() 内で行われます。

前の例のように、worker はその手順を正常に終了し、finished() シグナルを発行します。これは、スレッドの quit() スロットにも接続されているはずです。ただし、プログラムがスレッドを待機してハングするため、何らかの方法でスロットを実行してはなりません。前の例とは異なり、moveToThread プロシージャを再配置しても問題は解決しません。Worker::finished() と QThread::quit() の間の接続に Qt::DirectConnection タイプを使用する場合にのみ、すべてが機能します。理由がわかりません。

main.cpp: 上と同じ

プログラム.h:

#ifndef _PROGRAM_H_
#define _PROGRAM_H_

#include <QThread>
#include <QTimer>
#include "worker.h"

class Program: public QObject {
  Q_OBJECT

  private:
    QTimer *timer, *quittingTimer;
    Worker *worker;
    QThread *thread;

  public:
    ~Program();

    void execute();

  private slots:
    void quit();
    void update();
};

#endif // _PROGRAM_H_

プログラム.cpp:

#include <iostream>
#include <QCoreApplication>
#include "program.h"
#include "worker.h"

using namespace std;

Program::~Program() {
  delete timer;
  delete quittingTimer;
}

void Program::execute() {
  timer = new QTimer();
  timer->setInterval(500);
  connect(timer, SIGNAL(timeout()), this, SLOT(update()));

  worker = new Worker;
  thread = new QThread;

  cout << "Connection established: "
        << connect(thread, SIGNAL(started()), worker, SLOT(process()))
        << endl;

  // doesn't work if I remove Qt::DirectConnection
  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), thread, SLOT(quit()),
                    Qt::DirectConnection)
        << endl;

  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), this, SLOT(quit()))
        << endl;

  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()))
        << endl;

  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), thread, SLOT(deleteLater()))
        << endl;

  worker->moveToThread(thread);

  timer->start();
  thread->start();

  // simulates user pressing key to close program
  quittingTimer = new QTimer();
  quittingTimer->singleShot(4000, this, SLOT(quit()));
}

void Program::quit() {
  cout << "timer->stop()" << endl;
  timer->stop();

  cout << "worker->quit()" << endl;
  worker->quit();

  cout << "thread->wait()" << endl;
  thread->wait();

  cout << "qcore->quit()" << endl;
  QCoreApplication::quit();
}

void Program::update() {
  cout << "Program::update() called" << endl;
}

#include "program_moc.cpp"

worker.h:

#ifndef _WORKER_H_
#define _WORKER_H_

#include <QObject>

class Worker: public QObject {
  Q_OBJECT
  private:
    bool quit_flag;

  public:
    void quit();

  public slots:
    void process();

  signals:
    void finished();
};

#endif // _WORKER_H_

ワーカー.cpp:

#include <iostream>
#include <unistd.h>
#include <QThread>
#include "worker.h"

using namespace std;

void Worker::quit() {
  quit_flag = true;
}

void Worker::process() {
  quit_flag = false;
  while(!quit_flag) {
    cout << "Worker::process() is processing" << endl;
    usleep(300000);
  }
  cout << "Worker::finished() is being sent" << endl;
  emit finished();
  cout << "Worker::finished() is sent" << endl;
}

#include "worker_moc.cpp"

編集2

@ariwez のリンクの記事を読み直したところ、この 2 番目の例の問題点がわかりました。問題は、スレッドの QThread::finished() シグナルの待機中にメイン イベント ループが中断されたため、Worker::finished() シグナルを QThread::quit() スロットにディスパッチできなかったことです。ええ、基本的に私は行き詰まりました。

4

2 に答える 2

2

接続する前に moveToThread を実行するため、すべてが 1 つのスレッドになります。

于 2013-08-07T12:03:13.163 に答える