QT signals und slot mit threads

Alles rund um die Programmierung mit Qt
Antworten
Superheftig
Beiträge: 63
Registriert: 6. September 2008 15:20

QT signals und slot mit threads

Beitrag 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?
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: QT signals und slot mit threads

Beitrag 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.
Superheftig
Beiträge: 63
Registriert: 6. September 2008 15:20

Beitrag 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.
Dateianhänge
callstack.jpg
callstack.jpg (98.49 KiB) 6685 mal betrachtet
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag 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
Superheftig
Beiträge: 63
Registriert: 6. September 2008 15:20

Beitrag 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.
Dateianhänge
callstack2.jpg
callstack2.jpg (66.06 KiB) 6676 mal betrachtet
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag 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 :D Also wenns immer noch bockt muss ich passen :/
Superheftig
Beiträge: 63
Registriert: 6. September 2008 15:20

Beitrag 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
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag 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
:wink:
Superheftig
Beiträge: 63
Registriert: 6. September 2008 15:20

Beitrag von Superheftig »

jo geht!

Wie soll man denn da bitte drauf kommen ;)
Aber wenn man drüber nach denk macht es natürlich sinn.
nierth
Beiträge: 30
Registriert: 19. November 2008 22:56

Beitrag 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
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag 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..
Antworten