Seite 1 von 1
QT signals und slot mit threads
Verfasst: 19. Mai 2009 14:46
von Superheftig
Ich habe ein problem mit signals und slots bei benutztung von mehreren threads. Ich hab ein kleines beispielprogramm geschrieben um das ganze mal zu testen...funktioniert aber leider nicht so wie es soll
Code: Alles auswählen
class MyThread : public QThread
{
Q_OBJECT
public slots:
void myThreadSlot() {
qDebug() << "Slot";
}
signals:
void myThreadSignal();
private:
void run() {
emit myThreadSignal();
exec();
}
};
Der Thread emitiert in der run methode ein signal dass mit dem slot des threads über eine queuedconnection verbunden ist
Code: Alles auswählen
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread *myThread = new MyThread();
QObject::connect(myThread, SIGNAL(myThreadSignal()), myThread2, SLOT(myThreadSlot()), Qt::QueuedConnection);
myThread->start();
a.exec();
delete myThread;
}
So wie ich das verstanden habe sollte der slot nun in MyThread aufgerufen werden und nicht im MainThread von der CoreApp laufen.
Wenn ich allerdings an dem qDebug() << "Slot" einen breakpoint setzte und mir den aufrufsteak anschaue, geht der aufruf von QCoreApp::run und nicht von MyThread::run aus.
Wie kann das sein und was muss ich anders machen damit der slot in MyThread läuft?
Re: QT signals und slot mit threads
Verfasst: 19. Mai 2009 15:04
von franzf
Code: Alles auswählen
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread *myThread = new MyThread();
QObject::connect(myThread, SIGNAL(myThreadSignal()), myThread2, SLOT(myThreadSlot()), Qt::QueuedConnection);
myThread->start();
a.exec();
delete myThread;
}
Ist das ein Kopierfehler? Denn nirgendwo wird das Empfängerobjekt myThread2 erstellt. Sollte eine Meldung in der Konsole ausspucken.
Außerdem ist void run() nur protected und nicht private. K.A. ob das jetzt Probleme geben kann...
In jedem Fall ist QueuedConnection genau richtig um den Slot im Context des Threads aufzurufen, solange du ne Threadeigene Eventloop hast, was ja durch das exec() im run() gewährleistet ist, angestoßen mit myThread->start(). Ist bei dir alles der Fall, sollte also passen.
Verfasst: 19. Mai 2009 15:14
von Superheftig
ja das mit dem mythread2 ist nur ein kopierfehler...hatte das ganze auch mal mit 2 thread getestet.
run protected zu machen bringt leider auch nichts, habs mal ausprobiert.
Hab mal nen bild von dem callstack gemacht zum zeitpunkt der qDebug ausgabe. Das ganze wird definitiv nicht in Mythread run ausgeführt.
Verfasst: 19. Mai 2009 15:22
von franzf
Passt doch

Du startest mit deiner main, erster Funktionsaufruf ist QCoreApplication::exec(). Dann events processed. Per QMetaCall wird dann am Ende wird am Ende im Kontext des Threads der slot aufgerufen. Schau auch mal:
http://doc.trolltech.com/4.5.1/qmetaobj ... vokeMethod
Verfasst: 19. Mai 2009 15:39
von Superheftig
so ganz versteh ich das nicht. Wenn ich anstatt ne queued connection eine direct connection benutzte wird der slot ja sofort aufgerufen und dann sieht das ganze so aus. (s.u.)
also versteh ich nicht warum bei einer queued connection der aufruf nicht ebenfalls von dem MyThread::run ausgeht.
Auf das Problem bin ich überhaupt erst gestoßen als ich die Threads terminieren lassen wollte. Ich habe myThread.exit() aufgerufen, der Thread hat den eventloop und die run methode verlassen. Trotzdem liefen die slots weiter. Die original slot methode hatte sich immer wieder selber aufgerufen
Code: Alles auswählen
void myThreadSlot() {
..... Code ....
emit myThreadSignal();
}
Also wie kann das weiter laufen wenn der Thread keine events mehr empfangen kann weil der loop ja gestoppt ist.
Verfasst: 19. Mai 2009 16:21
von franzf
Gib dir mal in MyThread::myThreadSlot() den Wert von QThread::currentThread aus und vergleich ihn mit der im main().
Also in main() und dem Thread-Slot
Code: Alles auswählen
qDebug() << "Main resp. MyThread" << QThread::currentThread();
Dann siehst du gleich, ob dein Slot im Context von MyThread läuft, oder im Haupthread...
Aber ich bin nicht sehr bewandert in Thread-Problemen. Hab das selber bisher nur minimal (eher zu Testzwecken) gebraucht, und kenn das Meiste nur aus der Doku

Also wenns immer noch bockt muss ich passen :/
Verfasst: 19. Mai 2009 16:38
von Superheftig
Wenn ich in MyThread::run() vor dem exec() eine Ausgabe mache also
Code: Alles auswählen
void run() {
qDebug() << QThread::currentThread();
emit myThreadSignal();
exec();
}
krieg ich folgendes: MyThread(0x1a5ce80)
bei dem slot also
Code: Alles auswählen
void myThreadSlot() {
qDebug() << QThread::currentThread();
emit myThreadSignal();
}
bekomm ich: QThread(0x1a55318)
also läuft das ganze nicht in dem kontent von MyThread. Das zeigen ja auch genau die callstacks
Verfasst: 19. Mai 2009 17:35
von solarix
Das Problem ist vermutlich, dass der "this"-Pointer in der run()-Methode noch immer dem Hauptthread gehört (diese Instanz wurde ja in main() erstellt)..
Dies kannst du prüfen mit:
Code: Alles auswählen
void run() {
qDebug() << QThread::currentThread() << thread();
Um die emittierende Instanz (in diesem Fall "this") in den laufenden Thread zu verschieben, kannst du "moveToThread()" verwenden:
Code: Alles auswählen
MyThread *myThread = new MyThread();
MyThread *myThread2 = new MyThread();
myThread->moveToThread(myThread);
myThread2->moveToThread(myThread2);
Dann klappts auch mit dem Signal

Verfasst: 19. Mai 2009 18:04
von Superheftig
jo geht!
Wie soll man denn da bitte drauf kommen

Aber wenn man drüber nach denk macht es natürlich sinn.
Verfasst: 10. August 2009 10:54
von nierth
Hallo,
das moveToThread hat bei mir ebenfalls geklappt, allerdings habe ich nur einen vagen Schimmer was da vor sich geht. Kann mir irgendjemand die ganze Problematik nochmal kurz und verständlich erklären?
Danke schon im Voraus,
Thomas
Verfasst: 10. August 2009 13:02
von solarix
Das ist eigentlich unter
http://doc.trolltech.com/4.5/threads.ht ... event-loop beschrieben.. ich fasse mal zusammen:
- Zu jedem Thread gehört ein Eventloop
- Jedes QObject gehört zu einem Thread und damit auch zu einem bestimmten Eventloop
- Signals, welche einen Eventloop benötigen (also keine "DirectConnections") werden über den zum emittierenden Objekt gehörenden Eventloop abgewickelt (_nicht_ über den aktuellen Threadkontext)
Soweit so gut. Für eine von QThread abgeleitete Klasse bedeutet dies:
Die Instanz der Klasse wird ja immer in einem anderen Thread erstellt. Beispiel:
Code: Alles auswählen
int main (..)
{
..
MyThread t; // Instanz "t" wird im Main-Thread erstellt und gehört damit zum Main-Thread;
....
app.exec(); // Eventloop des Main-Threads
}
Wenn nun also diese Klasse in irgend einer Methode ein Signal wirft, wird dieses über den Eventloop des Main-Threads abgewickelt. Damit es jedoch über den Eventloop des Threads ("run() { exec(); // hier) }") abgewickelt wird, muss auch die Zugehörigkeit vom Mainthread (wo die Instanz erstellt wurde) auf den arbeitenden Thread (run-Methode der Thread-Klasse) geändert werden. Eben mit "moveToThread()".
hth..