[gelöst] QThread beenden mit aufräumen

Alles rund um die Programmierung mit Qt
Antworten
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

[gelöst] QThread beenden mit aufräumen

Beitrag von Willi2793 »

Hallo,

ich habe immer noch Probleme mit meinen QThreads. Ich definiere das ganze jetzt folgendermassen:

Code: Alles auswählen

 QThread* thread = new QThread(this);
 ConnectionTest* ct = new ConnectionTest();

 ct->moveToThread(thread);

 connect(thread, SIGNAL(started())      , ct    , SLOT(startWork()));

 connect(ct    , SIGNAL(ended())        , ct    , SLOT(deleteLater()));
 connect(ct    , SIGNAL(destroyed())    , thread, SLOT(quit()));

 connect(thread, SIGNAL(finished())     , thread, SLOT(deleteLater()));
 connect(thread, SIGNAL(terminated())   , thread, SLOT(deleteLater()));

 thread->start();
Das Ganze läuft in einer Schleife recht oft ab. Es ist ja ein Streß-Test für eine Server-Anwendung.

Der Slot "startWork()" in ConnectionThread startet nun die Verarbeitung und wirft an deren Ende das "ended()" Signal aus. Das Aufräumen in dieser Klasse funktioniert auch und die Verbindung zum Server wird sauber abgebaut. Das "Ended()"-Signal soll dann die Ganzen oben verketten Aufräumarbeiten auslösen. Das scheint auch im Großen und Ganzen ganz gut zu funktionieren. Allerdings bekomme ich sporadisch die Meldung:
QThread: Destroyed while thread is still running
Und ich verstehe jetzt nicht warum. Ich rufe das deleteLater doch erst auf wenn der Thread fertig ist!?

Grüße,
Willi
Zuletzt geändert von Willi2793 am 10. April 2011 22:26, insgesamt 1-mal geändert.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Du löschst deinen Thread obwohl er noch in seiner Eventloop ist - was erwartest Du?
Siehe http://doc.trolltech.com/4.7/qthread.html#run und http://doc.trolltech.com/4.7/qthread.html#quit
Wenn ended() QThread::quit() aufrufen würde so wie es in der Doku steht... naja dann müsste es gehen.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Ist

Code: Alles auswählen

connect(thread, SIGNAL(finished())     , 
            thread, SLOT(deleteLater()),
            Qt::QueuedConnection); 
besser?
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

Beitrag von Willi2793 »

Christian81 hat geschrieben:Du löschst deinen Thread obwohl er noch in seiner Eventloop ist - was erwartest Du?
Siehe http://doc.trolltech.com/4.7/qthread.html#run und http://doc.trolltech.com/4.7/qthread.html#quit
Wenn ended() QThread::quit() aufrufen würde so wie es in der Doku steht... naja dann müsste es gehen.
Wieso lösche ich ihn wenn er noch in der Eventloop ist? Ich rufe das deleteLater doch auf wenn das finished()-Signal von ihm geworfen wurde. Danach ist seine Eventloop doch fertig? Wo habe ich denn da den Denkfehler? Das "quit()" vom thread wird ja schon vorher aufgerufen. Die Reihenfolge sollte sein:

ct wirft ended() -> ct->deleteLater()
ct wirft destroyed() -> thread->quit()
thread wirft finished() --> thread->deleteLater()

Da denke ich sollte der thread incl. Eventloop beendet sein wenn das deleteLater() aufgerufen wird!?
solarix hat geschrieben:Ist

Code: Alles auswählen

connect(thread, SIGNAL(finished())     , 
            thread, SLOT(deleteLater()),
            Qt::QueuedConnection); 
besser?
Probiere ich morgen. Der Rechner mit QT ist jetzt nicht da ;) Aber danke für die Anregung. Werde berichten

Grüße,
Willi
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

Beitrag von Willi2793 »

Nein, den Connectiontyp ändern bringt keine Veränderung. Schade.

Weitere Vorschläge? :wink:
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Was erwartest Du denn noch

Code: Alles auswählen

connect(ct    , SIGNAL(ended())        , ct    , SLOT(deleteLater())); 
Dein Thread sendet ended(), ist aber noch in der Eventloop da noch kein quit() aufgerufen wurde und Du löschst ihn... :roll:
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

Beitrag von Willi2793 »

Christian81 hat geschrieben:Was erwartest Du denn noch

Code: Alles auswählen

connect(ct    , SIGNAL(ended())        , ct    , SLOT(deleteLater())); 
Dein Thread sendet ended(), ist aber noch in der Eventloop da noch kein quit() aufgerufen wurde und Du löschst ihn... :roll:
Ah, da ist das Misverständnis. Ich gebe allerdings auch zu das ich nicht explizit darauf hingewiesen habe. "ct" ist eine von QObject abgeleitete Klasse und wird mit moveToThread einem QThread zugewiesen. Das Ende von ct wird mit dem ended() angezeigt. Danach wird ct aufgeräumt und wenn das aufgeräumt ist wird das Objekt von QThread (hier "thread") aufgeräumt. Deshalb ist das deleteLater() für ct kein löschen des QThread.

War es das was Du meintest?
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Jetzt habe ich das Konstrukt kapiert... :)
Da bleibt wohl nur ein Breakpoint im Qt-Code an der Stelle an der die Warnung ausgegeben wird. Oder ein minimales Beispiel damit wir es auch mal testen können.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

Beitrag von Willi2793 »

So, ich habe dann mal Client und Server zusammen gestaucht. Der Server ist zwar immer noch etwas umfangreich, aber um den geht es auch nicht. Der Client macht mir das Kopfzerbrechen.

Vielen Dank für Eure Bemühungen.

In der GUI des Client einfach eine Zahl eingeben (ich teste immer mit 3) und dann auf den Button klicken. Es wird eine Verbindung zum Server hergestellt (localhost auf Port 36001) und 3mal Daten hin und her geschickt. Und das halt ständig bis der Client beendet wird (Close-Button des Window)
Dateianhänge
Test.zip
(21.58 KiB) 219-mal heruntergeladen
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

So... hat ein wenig gedauert aber ich habs gefunden :)
Es liegt an deinem Testcase - der Rest ist ok so (abgesehen vom fehlenden delete der ConnectionTest - Objekte)

Hier liegt das Problem:

Code: Alles auswählen

    for(...
        connect(this  , SIGNAL(startThreads()) , thread, SLOT(start()));
    }
    emit startThreads();
Wenn ein Thread noch nicht gelöscht wurde, Du aber on_process_clicked() schon aufrufst bekommt der alte noch lebende Thread auch ein start() - Signal und wird noch einmal gestartet... :)
Ein 'disconnect(SIGNAL(startThreads()));' vor der for-Schleife oder gleich das connect komplett weglassen und start() direkt aufrufen und schon gehts.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

Beitrag von Willi2793 »

Christian81 hat geschrieben:So... hat ein wenig gedauert aber ich habs gefunden :)
Es liegt an deinem Testcase - der Rest ist ok so (abgesehen vom fehlenden delete der ConnectionTest - Objekte)
Das delete ist dem Abspecken zum Opfer gefallen. Im richtigen Programm ist es drin.
Christian81 hat geschrieben:Hier liegt das Problem:

Code: Alles auswählen

    for(...
        connect(this  , SIGNAL(startThreads()) , thread, SLOT(start()));
    }
    emit startThreads();
Wenn ein Thread noch nicht gelöscht wurde, Du aber on_process_clicked() schon aufrufst bekommt der alte noch lebende Thread auch ein start() - Signal und wird noch einmal gestartet... :)
Ein 'disconnect(SIGNAL(startThreads()));' vor der for-Schleife oder gleich das connect komplett weglassen und start() direkt aufrufen und schon gehts.
So simpel und doch so kompliziert zu finden. Da hätte ich noch eine Weile (erfolglos) gesucht denke ich.

Super, vielen Dank :)
Antworten