2013-12-17 9 views
5

Ich habe Probleme mit Qt Threads und Verbindungen. Ich fand mehrere Tutorials und Diskussionen zu diesem Thema, ich folgte this tutorial, um den Thread zu erstellen. Aber ich habe immer noch das Problem, dass das Aufrufen von wait() auf dem Thread nie zurückkehrt und die Benutzeroberfläche einfriert.QThread :: wait() kehrt nicht ohne direkte Verbindung zurück

Eine ähnliche Frage gestellt wurde hier vor (das zweite Beispiel): Qt connection type between threads: why does this work?

In der letzten Bearbeitung der Frage, erwähnt der Autor, dass er eine Sackgasse geschaffen hatte. Ich nehme an, ich mache das gleiche in meiner Bewerbung. Aber ich verstehe immer noch nicht, warum das passiert. Das Lesen der suggested article half mir nicht zu verstehen. Ich habe gerade verstanden, dass Deadlocks passieren können, aber ich weiß nicht, was es dort oder in meinem Fall verursacht.

ich geschaffen habe auch ein Beispiel, das auf das Kernproblem reduziert wird. Suchen Sie den Code am Ende dieser Frage.

Also meine Fragen sind: Was genau die Ursache für den Stillstand in meinem Beispiel ist? Gibt es eine Lösung, ohne die Verbindung direkt zu verbinden?

Ich würde wirklich irgendwelche Hinweise zu schätzen wissen.

Danke!

EDIT:

Aufgrund der Kommentare I versucht, die Stoppanforderung über ein Signal zu senden und I hinzugefügt, um einen QCoreApplication :: process() -Aufruf in der Fadenschlaufe. Aber das Hauptproblem ist immer noch das gleiche.

EDIT2:

ich eine akzeptable Lösung gefunden, nach ein wenig mehr über Event denken Schleifen:

thread.requestStop(); 

// now instead of using wait(), we poll and keep the event loop alive 
// polling is not nice, but if it does not take a very long time 
// for the thread to finish, it is acceptable for me. 
while (thread.isRunning()) 
{ 
    // This ensures that the finished() signal 
    // will be processed by the thread object 
    QCoreApplication::processEvents();   
} 

Das funktioniert tatsächlich und der Arbeiter selbst steuert, wie funktioniert.

Nachdem mit diesem kommen, ich habe auch eine Erklärung für das Einfrieren Problem: Warten Aufruf des Haupt-Thread entweder besetzt oder suspendiert zu halten scheint, so dass es keine Ereignisse verarbeiten hat. Da das Thread-Objekt im Haupt-Thread lebt, wird das finished() -Signal des Threads abgefragt, aber nie verarbeitet.

Meine implizite Annahme, dass thread.wait() noch die Ereignisschleife weiter arbeiten würde, war offensichtlich falsch. Aber für was ist die QThread :: wait() - Funktion gut?!? Diese

ist nur eine Theorie, aber vielleicht jemand hier kann es verifizieren oder falsifizieren ...

EDIT 3 (Endlösung):

Nach this small article Lesen und eine Subklassifizieren Lösung implmenting, ich glaube, dass dies für dieses spezielle Problem vorzuziehen ist. Es gibt keine Notwendigkeit für eine Ereignisschleife und mir geht es gut mit direkten Aufrufen auf einem anderen Thread und Mutex-Schutz verwenden. Es ist weniger Code, einfacher zu verstehen und einfacher zu debuggen.

Ich denke, ich würde die Nicht-Unterklassen-Strategie nur verwenden, wenn es mehr Interaktion mit dem Thread als nur starten und pausieren würde.


Mein reduziert Beispiel

Vielleicht sollte ich darauf hinweisen, dass ich nicht löschen den Faden, weil in meiner ursprünglichen Anwendung, wie ich später wieder aufnehmen wollen, so dass es tatsächlich zu stoppen, bedeutet es eine Pause.

worker.h:

#ifndef WORKER_H 
#define WORKER_H 

#include <QObject> 
#include <QMutex> 

class Worker : public QObject 
{ 
    Q_OBJECT 

public: 
    explicit Worker(QObject* parent = NULL); 

public slots: 
    void doWork(); 
    void requestStop(); 

signals: 
    void finished(); 

private: 

    bool stopRequested; 
    QMutex mutex; 
}; 

#endif // WORKER_H 

worker.cpp:

#include "worker.h" 

#include <QThread> 
#include <iostream> 

using namespace std; 

Worker::Worker(QObject *parent) 
    : stopRequested(false) 
{ 
} 

void Worker::doWork() 
{ 
    static int cnt = 0; 

    // local loop control variable 
    // to make the usage of the mutex easier. 
    bool stopRequesteLocal = false; 

    while (!stopRequesteLocal) 
    { 
     cout << ++cnt << endl; 
     QThread::msleep(100); 

     mutex.lock(); 
     stopRequesteLocal = stopRequested; 
     mutex.unlock(); 
    } 

    cout << "Finishing soon..." << endl; 

    QThread::sleep(2); 
    emit finished(); 
} 

void Worker::requestStop() 
{ 
    mutex.lock(); 
    stopRequested = true; 
    mutex.unlock(); 
} 

Hauptprogramm:

#include <QCoreApplication> 
#include <QThread> 
#include <QtCore> 
#include <iostream> 

#include "worker.h" 

using namespace std; 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 
    QThread thread; 
    Worker worker; 


    QObject::connect(&thread, SIGNAL(started()), &worker, SLOT(doWork())); 

    // this does not work: 
    QObject::connect(&worker, SIGNAL(finished()), &thread, SLOT(quit())); 

    // this would work: 
    //QObject::connect(&worker, SIGNAL(finished()), &thread, SLOT(quit()), Qt::DirectConnection); 

    // relocating the moveToThread call does not change anything. 
    worker.moveToThread(&thread); 

    thread.start(); 

    QThread::sleep(2); 

    worker.requestStop(); 
    cout << "Stop requested, wait for thread." << endl; 
    thread.wait(); 
    cout << "Thread finished" << endl; 

    // I do not know if this is correct, but it does not really matter, because 
    // the program never gets here. 
    QCoreApplication::exit(0); 
} 
+0

'Schlaf' a in Qt geschützt ist, wie kann man QThread :: Schlaf (2) nennen; ?? – UmNyobe

+0

Ich weiß es nicht. Ich mache es einfach und es funktioniert. ;) – Kanalpiroge

+0

Das ist nicht möglich :) Hast du die Qt-Quelle bearbeitet? –

Antwort

2

habe ich meine eigenen answere auf die Frage Text EDIT 3.

0

Es sieht nicht so aus, wie Sie völlig diesen Artikel gelesen haben.

QThread* thread = new QThread; 
Worker* worker = new Worker(); 
worker->moveToThread(thread); 
connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString))); 
connect(thread, SIGNAL(started()), worker, SLOT(process())); 
connect(worker, SIGNAL(finished()), thread, SLOT(quit())); 
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); 
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); 
thread->start(); 

Sie haben nur teilweise implementiert, was auf dem Artikel vorgeschlagen wird.

QThread::wait() warten, bis QThread::finished() von doWork() ausgegeben wird. Bitte senden Sie diese SIGNAL, wenn Sie aus diesem Thread beenden und zum Haupt-Thread zurückkehren müssen. Dazu müssen Sie einen Verweis auf den Thread behalten, auf den dieses Objekt verschoben wird.

+0

Das scheint für mich nicht zu funktionieren. Wenn ich die Zeile auszeichne, blockiert das Programm immer noch bei 'thread.wait()'. – Kanalpiroge

+0

zu Ihrer Bearbeitung: Ja, weil ich nicht möchte, dass die Objekte später gelöscht werden. Aber ich habe diese Dinge nur ausprobiert, um zu sehen, ob es einen Unterschied macht, aber das tut es nicht. Ich habe auch versucht, Zeiger zu verwenden, anstatt die Objekte auf dem Thread zu erstellen, aber das ändert auch nichts. – Kanalpiroge

+0

Das fertige() Signal wird am Ende von doWork() ausgegeben, nachdem die Schleife beendet ist. Aber trotzdem, wait() kehrt nicht zurück. Aber ich bin mir sicher, dass das Programm die Schleife verlässt, denn nach dem Aufruf von requestStop() erscheint "finishing soon ..." in der Konsole. – Kanalpiroge

2

Das erste Problem, das ich sehe, ist, dass Sie keine Signale und Slots verwenden, um zwischen den Objekten zu kommunizieren, die auf verschiedenen Threads ausgeführt werden; main und der neue Thread, der das Worker-Objekt hostet.

Sie bewegen den Arbeiter Objekt auf den zweiten Thread, sondern eine Funktion der betreffende Arbeitnehmer Objekt aus dem Haupt-Thread aufrufen: -

thread.start(); 
QThread::sleep(2); 
worker.requestStop(); // Aaahh, this is running on the new thread!!! 

Bedenkt man, dass ein Thread seinen eigenen Stack und Register hat ich nicht wirklich tun Sehen Sie, wie das sicher ist.

Wenn Sie Signale und Slots verwenden, behandelt Qt viele Threading-Probleme. Während Sie in der Lage sein sollten, eine Variable zu verwenden, um den 2. Thread zu steuern, wäre es auch sauberer, Signale und Slots zu verwenden.

Beachten Sie, dass beim Senden eines Signals von einem Thread eine Nachricht an den Thread des empfangenden Objekts gesendet wird, wenn sich der Absender und der Empfänger auf separaten Threads befinden.

Konvertieren Sie Ihren Code, um Signale und Steckplätze für die Kommunikation zwischen Objekten auf verschiedenen Threads zu verwenden, und Ihr Deadlock sollte verschwinden.

+0

Ich würde denken, dass die Funktion requestStop() aufgrund des Mutex threadsafe ist. Und wenn das wirklich ein Punkt war, erklärt es nicht, warum eine direkte Verbindung (die eigentlich schlecht sein sollte) funktioniert, während eine Verbindung in der Warteschlange (die Sie verwenden sollten) nicht funktioniert. – Kanalpiroge

+1

Der Mutex schützt die Variable, die in der Funktion geändert wird, aber nicht den Stapel und die Register, die in dieser Funktion verwendet werden. Verwenden Sie auf jeden Fall eine gemeinsam genutzte Variable mit einem Mutex, aber ich schlage vor, dass Sie einen Slot am Worker-Objekt aufrufen, anstatt die Funktion direkt aufzurufen. Was die direkte Verbindung angeht, erwarte ich, dass es nur Glück ist und darauf wartet, später Probleme zu verursachen. Wenn jemand anders weiß, würde mich die Erklärung interessieren, wie das funktionieren kann. – TheDarkKnight

+1

@Kanalpiroge Merlin ist genau richtig, zumindest in diesem Punkt. Sie müssen die Anfrage über ein Signal stellen. – UmNyobe