Qt Verbindungstyp zwischen Threads: Warum funktioniert das?

Beim Versuch, ein System mit mehreren Kameras mit einem anderen Thread zu betreiben, der eine andere Kamera handhabt, konnte ich nicht feststellen, dass Signale und Slots zwischen verschiedenen Threads korrekt funktionieren. Ich wusste, dass etwas mit der Tatsache nicht stimmt, dass das Objekt, das das Signal sendet, und das Objekt des zugehörigen Slots in verschiedenen Threads leben, und daher wusste ich, dass ich wahrscheinlich nur einen geeigneten "Verbindungstyp" -Parameter für die Verbindung finden musste. Schließlich stellte ich fest, dass nur mit Qt :: DirectConnection alles so funktioniert, wie es sollte.

Hier finden Sie den vereinfachten Code. Hier ist eine kurze Beschreibung, wie alles funktionieren sollte.

Anwendung ist das Hauptprogramm, das alle Threads erstellen und starten soll. In dieser vereinfachten Version wartet es dann einfach darauf, dass der Arbeiter seine Jobs durch den Schlitz "Beenden" beendet.

Worker ist das Objekt, das eine der Thread-Aufgaben ausführt. In diesem vereinfachten Beispiel warte ich nur einige Zeit, bis die Berechnung abgeschlossen ist. Der Worker gibt dann ein Signal aus, das an die Anwendungsinstanz gerichtet ist. Diese darf dann auf alle Threads warten und QCoreApplication beenden.

Was ich finde ist, dass, wenn ich Qt :: DirectConnection nicht in der zweiten Verbindung verwende, das fertige () Signal des Arbeiters nicht den quit () Schlitz des Threads auslöst, was bedeutet, dass die Anwendung dann hängen bleibt Warten auf den Thread.

Meine Frage ist: warum ist das so? Sollte ich QueuedConnection oder etwas anderes verwenden, da die beiden Objekte (der Worker und der Thread) zu unterschiedlichen Threads gehören? Ich dachte, DirectConnection sollte nur für Objekte verwendet werden, die zum selben Thread gehören.

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;
}

program.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_

program.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_

worker.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"

BEARBEITEN

Die Antwort von @ariwez löst das Problem in diesem speziellen vereinfachten Beispiel, aber nicht in einem etwas komplexeren, das ich jetzt hinzufüge.

In diesem Beispiel

Das Programm hat seinen eigenen Job, der mithilfe eines QTimers regelmäßig ausgeführt wird. Das Programm hat noch einen weiteren QTimer, der verwendet wird, um den Benutzer beim Verlassen des Programms zu simulieren, wodurch die Ausführung des Slots Program :: quit () ausgelöst wird.

Der Mitarbeiter führt seinen eigenen Job aus, bis sein Beendigungsflag auf false gesetzt wird. Dies geschieht in Program :: quit ().

Wie im vorherigen Beispiel beendet der Worker die Prozedur erfolgreich und gibt das Signal finished () aus, das auch mit dem Slot quit () des Threads verbunden sein soll. Allerdings muss der Slot irgendwie nicht ausgeführt werden, da das Programm auf den Thread wartet. Anders als im vorherigen Beispiel wird das Problem durch das Verschieben der moveToThread-Prozedur nicht behoben: Alles funktioniert genau dann, wenn ich den Typ Qt :: DirectConnection für die Verbindung zwischen Worker :: finished () und QThread :: quit () und I verwende kann nicht verstehen warum.

main.cpp: wie oben

program.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_

program.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_

worker.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"

BEARBEITEN 2

Beim erneuten Lesen des Artikels in @ariwez 'Link habe ich herausgefunden, was das Problem in diesem zweiten Beispiel war. Das Problem bestand darin, dass die Hauptereignisschleife unterbrochen wurde, während auf das QThread :: finished () - Signal des Threads gewartet wurde, sodass das Worker :: finished () - Signal nicht in den QThread :: quit () - Slot gesendet werden konnte. Also ja, im Grunde habe ich mich festgefahren.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage