Frage

Die Dokumentation Qt für QThread sagt eine Klasse von QThread zu erstellen, und die run-Methode zu implementieren.

Im Folgenden genommen wird von der 4,7 QThread Dokumentation ...

  

Ihre eigenen Threads erstellen, Unterklasse QThread und reimplementieren run (). Zum Beispiel:

 class MyThread : public QThread
 {
 public:
     void run();
 };

 void MyThread::run()
 {
     QTcpSocket socket;
     // connect QTcpSocket's signals somewhere meaningful
     ...
     socket.connectToHost(hostName, portNumber);
     exec();
 }

So in jedem einzelnen Thread ich erstellt habe, habe ich genau das getan und für die meisten Dinge funktioniert es ganz gut (ich nicht implementieren moveToThread (dies) in jedem meiner Objekte und es funktioniert gut).

Ich schlug einen Haken letzte Woche (es geschafft, durch sie zu erhalten, indem Arbeit um, wo ich meine Objekte erstellt) und fand die folgenden Blog-Post . Hier ist im Grunde sagt, dass Subklassen QThread ist wirklich nicht der richtige Weg, es zu tun (und dass die Dokumentation falsch ist).

Dieses wird von einem Qt-Entwickler kommen so auf den ersten Blick war ich interessiert und auf weitere Überlegungen stimmen mit ihm. Nach OO Prinzipien, wollen Sie wirklich nur eine Klasse, Unterklasse weiter, dass die Klasse zu verbessern ... nicht nur die Methoden Klassen direkt verwenden ... das ist, warum Sie instantiate ...

Lassen Sie uns sagen wollte ich eine benutzerdefinierte QObject Klasse auf einen Thread bewegen ... was die ‚richtige‘ Art und Weise, es zu tun wäre? In dieser Blog-Post, er sagt 'er ein Beispiel irgendwo hat ... aber wenn jemand weiter es mir erklären könnte es sehr zu schätzen würde!

Update:

Da diese Frage so viel Aufmerksamkeit bekommt, hier ist eine Kopie, und fügen Sie der 4.8-Dokumentation mit dem ‚richtigen‘ Weg, um eine QThread zu implementieren.

class Worker : public QObject
 {
     Q_OBJECT
     QThread workerThread;

 public slots:
     void doWork(const QString &parameter) {
         // ...
         emit resultReady(result);
     }

 signals:
     void resultReady(const QString &result);
 };

 class Controller : public QObject
 {
     Q_OBJECT
     QThread workerThread;
 public:
     Controller() {
         Worker *worker = new Worker;
         worker->moveToThread(&workerThread);
         connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
         connect(this, SIGNAL(operate(QString)), worker, SLOT(doWork(QString)));
         connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
         workerThread.start();
     }
     ~Controller() {
         workerThread.quit();
         workerThread.wait();
     }
 public slots:
     void handleResults(const QString &);
 signals:
     void operate(const QString &);
 };

Ich glaube immer noch, dass es sich lohnt, darauf zu hinweisen, dass sie ein zusätzliches Worker::workerThread Mitglied umfassen, die nicht notwendig ist und wird nie in ihrem Beispiel verwendet. Entfernen Sie das Stück und es ist ein gutes Beispiel dafür, wie in Qt zu tun Threading.

War es hilfreich?

Lösung

Über das einzige, was ich hinzufügen denken kann, ist weiter fest, dass QObjects eine Affinität mit einem einzigen Thread. Dies ist normalerweise der Thread, der die QObject schafft. Wenn Sie also eine QObject in der App-Haupt-Thread erstellen und wollen es in einem anderen Thread verwenden, müssen Sie moveToThread() verwenden, um die Affinität zu ändern.

Das spart bis zu Unterklasse QThread mit und Ihre Objekte in dem run() Verfahren zu schaffen, damit Sie Ihre Sachen gut verkapselt zu halten.

Der Blog-Eintrag enthält einen Link zu einem Beispiel . Es ist ziemlich kurz, aber es zeigt die Grundidee. Erstellen Sie Ihre QObjects, schließen Sie die Signale, erstellen Sie Ihre QThread, bewegen Sie QObjects zum QThread und den Thread starten. Das Signal / Slot-Mechanismen sorgen dafür, dass Faden Grenzen richtig gekreuzt und sicher.

Sie können die Synchronisation einführen müssen, wenn Sie Anruf Methoden auf Ihr Objekt außerhalb dieses Mechanismus haben.

Ich weiß, hat Qt einige andere nette Threading Einrichtungen über Themen, die wahrscheinlich lohnt sich vertraut mit, aber ich habe noch zu tun:)

Andere Tipps

Hier ein Beispiel dafür, wie QThread richtig verwenden , aber es hat einige Probleme mit ihm, die in den Kommentaren widerspiegeln. Da insbesondere in welcher Reihenfolge die Schlitze ausgeführt werden, nicht genau definiert wird, könnte es zu verschiedenen Problemen führen. Der Kommentar veröffentlicht am 6. August, gibt 2013 eine nette Idee, wie man mit diesem Thema befassen. Ich benutze so etwas in meinem Programm, und hier einig Beispiel-Code zu klären.

Die Grundidee ist die gleiche. Ich habe QThread Beispiel das Leben in meinem Haupt-Thread zu erstellen, ein Arbeiter Klasseninstanz das Leben in den neuen Thread habe ich, und dann alle die Signale I connect

void ChildProcesses::start()
{
    QThread *childrenWatcherThread = new QThread();
    ChildrenWatcher *childrenWatcher = new ChildrenWatcher();
    childrenWatcher->moveToThread(childrenWatcherThread);
    // These three signals carry the "outcome" of the worker job.
    connect(childrenWatcher, SIGNAL(exited(int, int)),
            SLOT(onChildExited(int, int)));
    connect(childrenWatcher, SIGNAL(signalled(int, int)),
            SLOT(onChildSignalled(int, int)));
    connect(childrenWatcher, SIGNAL(stateChanged(int)),
            SLOT(onChildStateChanged(int)));
    // Make the watcher watch when the thread starts:
    connect(childrenWatcherThread, SIGNAL(started()),
            childrenWatcher, SLOT(watch()));
    // Make the watcher set its 'stop' flag when we're done.
    // This is performed while the watch() method is still running,
    // so we need to execute it concurrently from this thread,
    // hence the Qt::DirectConnection. The stop() method is thread-safe
    // (uses a mutex to set the flag).
    connect(this, SIGNAL(stopped()),
            childrenWatcher, SLOT(stop()), Qt::DirectConnection);
    // Make the thread quit when the watcher self-destructs:
    connect(childrenWatcher, SIGNAL(destroyed()),
            childrenWatcherThread, SLOT(quit()));
    // Make the thread self-destruct when it finishes,
    // or rather, make the main thread delete it:
    connect(childrenWatcherThread, SIGNAL(finished()),
            childrenWatcherThread, SLOT(deleteLater()));
    childrenWatcherThread->start();
}

Einige Hintergrundinformationen:

Die ChildProcesses Klasse ist ein Kind-Prozess-Manager, der mit Laich neuem Kindprozess beginnt () fordert, hält die Liste der laufenden Prozesse und so weiter. Allerdings muss es den Überblick über die Kinder Staaten halten, was bedeutet, waitpid () -Aufruf auf Linux oder WaitForMultipleObjects auf Windows. Früher habe ich diese in nicht-blockierenden Modus rufen Sie einen Timer, aber jetzt will ich mehr prompte Reaktion, was bedeutet Modus blockiert. Das ist, wo der Faden kommt in.

Die ChildrenWatcher Klasse wird wie folgt definiert:

class ChildrenWatcher: public QObject {
    Q_OBJECT
private:
    QMutex mutex;
    bool stopped;
    bool isStopped();
public:
    ChildrenWatcher();
public slots:
    /// This is the method which runs in the thread.
    void watch();
    /// Sets the stop flag.
    void stop();
signals:
    /// A child process exited normally.
    void exited(int ospid, int code);
    /// A child process crashed (Unix only).
    void signalled(int ospid, int signal);
    /// Something happened to a child (Unix only).
    void stateChanged(int ospid);
};

Hier ist, wie es funktioniert. Wenn all dieses Zeug gestartet wird, die Child-:: start () -Methode aufgerufen wird (siehe oben). Es schafft eine neue und eine neue QThread ChildrenWatcher, die dann auf den neuen Faden bewegt. Dann schließe ich drei Signale, die meinen Manager über das Schicksal seiner Kindprozesse informieren (beendet / signalisierte / Gott-weiß-was-passiert ist). Dann startet den Haupt Spaß.

I QThread verbinden :: gestartet () an der ChildrenWatcher :: watch () -Methode, so dass es so schnell gestartet, wie der Thread bereit ist. Da die Beobachter Leben in dem neuen Thread, das ist, wo die Uhr () -Methode ausgeführt wird (Warteschlange Verbindung wird verwendet, um den Schlitz zu nennen).

Dann schließe ich die ChildProcesses :: gestoppt () Signal an die ChildrenWatcher :: stop () Steckplatz mit Qt :: Direct, weil ich es asynchron tun müssen. Dies ist erforderlich, damit mein Thread stoppt, wenn der ChildProcesses Manager nicht mehr benötigt wird. Die Methode stop () sieht wie folgt aus:

void ChildrenWatcher::stop()
{
    mutex.lock();
    stopped = true;
    mutex.unlock();
}

Und dann ChildrenWatcher :: watch ():

void ChildrenWatcher::watch()
{
  while (!isStopped()) {
    // Blocking waitpid() call here.
    // Maybe emit one of the three informational signals here too.
  }
  // Self-destruct now!
  deleteLater();
}

Oh, und die isStopped () Methode ist nur eine bequeme Möglichkeit, einen Mutex in der while () Zustand zu verwenden:

bool ChildrenWatcher::isStopped()
{
    bool stopped;
    mutex.lock();
    stopped = this->stopped;
    mutex.unlock();
    return stopped;
}

Also, was hier passiert ist, dass ich die gestoppte-Flag gesetzt, wenn ich zu beenden müssen, und dann das nächste Mal isStopped () aufgerufen wird false zurückgegeben und die Fadenenden.

Also, was passiert, wenn die Uhr () Schlaufenende? Er fordert deleteLater (), so dass die Aufgabe selbst zerstört, sobald die Steuerung an die Faden Ereignisschleife zurückgeführt wird, die nur direkt nach dem deleteLater () Aufruf geschieht (wenn Uhr () zurückkehrt). Gehen wir zurück zu ChildProcesses :: start (), können Sie sehen, dass es eine Verbindung vom () Signal des Watcher zerstört ist die quit () -Slot des Gewindes. Dies bedeutet, dass der Faden automatisch beendet, wenn der Beobachter erfolgt. Und wenn es fertig ist, es sich selbst zerstört zu, weil seine eigene fertig () Signal an seinen deleteLater () Steckplatz angeschlossen ist.

Das ist so ziemlich die gleiche Idee wie Maya geschrieben, sondern weil ich die Selbstzerstörung Idiom verwenden, brauche ich nicht von der Reihenfolge abhängen, in der die Schlitze genannt werden. Es ist immer wieder selbst zu zerstören, später Stopp-Thread, dann ist es auch sich selbst zerstört. Ich könnte ein fertig (Signal) in dem Arbeiter, definieren und verbinden Sie es dann in seine eigenen deleteLater (), aber das würde nur bedeuten, eine Verbindung mehr. Da ich nicht ein fertiges () Signal für andere Zwecke brauche, wähle ich deleteLater nur nennen () aus dem Arbeiter selbst.

Maya erwähnt auch, dass Sie keine neue QObjects in den Arbeitern Konstruktor zuweisen sollten, weil sie in dem Thread nicht leben werden Sie den Arbeiter zu bewegen. Ich würde sagen, es trotzdem tun, denn dasIst der Weg OOP funktioniert. So stellen Sie sicher, dass alle diese QObjects sind Kinder des Arbeitnehmers (das heißt, verwenden Sie die QObject (QObject *) Konstruktor) - moveToThread () verschiebt alle die Kinder zusammen mit dem Objekt bewegt wird. Wenn Sie wirklich haben QObjects benötigen, die nicht Kinder des Objekts sind, dann überschreiben moveToThread () in der Arbeiter, so dass es auch alle notwendigen Dinge bewegt.

Um nicht zu schmälern @ sergey-tachenov des ausgezeichneten Antwort, aber in QT5 können Sie mit SIGNAL und SCHLITZ stoppen, vereinfachen Sie den Code und haben den Vorteil der Kompilierung Prüfung:

void ChildProcesses::start()
{
    QThread *childrenWatcherThread = new QThread();
    ChildrenWatcher *childrenWatcher = new ChildrenWatcher();
    childrenWatcher->moveToThread(childrenWatcherThread);
    // These three signals carry the "outcome" of the worker job.
    connect(childrenWatcher, ChildrenWatcher::exited,
            ChildProcesses::onChildExited);
    connect(childrenWatcher, ChildrenWatcher::signalled,
            ChildProcesses::onChildSignalled);
    connect(childrenWatcher, ChildrenWatcher::stateChanged,
            ChildProcesses::onChildStateChanged);
    // Make the watcher watch when the thread starts:
    connect(childrenWatcherThread, QThread::started,
            childrenWatcher, ChildrenWatcher::watch);
    // Make the watcher set its 'stop' flag when we're done.
    // This is performed while the watch() method is still running,
    // so we need to execute it concurrently from this thread,
    // hence the Qt::DirectConnection. The stop() method is thread-safe
    // (uses a mutex to set the flag).
    connect(this, ChildProcesses::stopped,
            childrenWatcher, ChildrenWatcher::stop, Qt::DirectConnection);
    // Make the thread quit when the watcher self-destructs:
    connect(childrenWatcher, ChildrenWatcher::destroyed,
            childrenWatcherThread, QThread::quit);
    // Make the thread self-destruct when it finishes,
    // or rather, make the main thread delete it:
    connect(childrenWatcherThread, QThread::finished,
            childrenWatcherThread, QThread::deleteLater);
    childrenWatcherThread->start();
}

die QThread Klasse Subklassen noch den Code in dem Ursprung Thread ausgeführt. Ich wollte einen udp Zuhörer in Anwendung auszuführen, die bereits den GUI-Thread verwendet (der Haupt-Thread) und während meines udp Zuhörer funktioniert perfekt meine GUI eingefroren wurde, wie es von dem subclassed QThread Event-Handler blockiert wurde. Ich denke, was g19fanatic geschrieben korrekt ist, aber Sie werden auch den Worker-Thread müssen erfolgreich das Objekt an den neuen Thread migrieren. Ich fand diese Post, die in Details des taten und tun nicht des Gewindes in QT beschreibt.

Must Read, bevor Sie QThread Unterklasse!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top