[gelöst] Asynchrones Beenden

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

[gelöst] Asynchrones Beenden

Beitrag von Willi2793 »

Hallo,

ich starte von main() aus einen Thread der wiederum eine variable Anzahl Sub-Threads startet.

Nun habe ich an aboutToQuit() das beenden des ersten Threads gehängt. Dabei sollen natürlich auch alle Sub-Threads beendet werden was aber unter Umständen ein bisschen dauern kann weil die aktuelle Aktion noch beendet werden muss. Das bedeutet ich beende die Sub-Threads asynchron.

Ich habe das Signal aboutToQuit() nun auch per BlockedQueued... connected. Aber wie kann ich darin nun die anderen Threads asynchron beenden? Oder muss ich auf jeden Thread einzeln warten? Was dann aber nicht mehr asynchron wäre.

Grüße,
Willi
Zuletzt geändert von Willi2793 am 29. März 2011 17:26, insgesamt 1-mal geändert.
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Du kannst threads auch länger laufen lassen, als wie ihr erzeugerthread existiert. Du musst nur sicherstellen, das der laenger laufende Thread voellig autark laufen kann, und nicht etwa auf ressourcen zugreift, die mit dem erzeugerthread ins nirvana verschwinden.

Das funktioniert, braucht man aber eigentlich nicht oft !

Normal wartet der erzeugerthread, bis sich seine child-threads beendet haben.
Oder muss ich auf jeden Thread einzeln warten?
Assynchron beenden, bedeutet in deinem Fall, du schiesst das beenden an alle Threads raus, und dann wartest, bis sich alle beendet haben, unabhaengig der reihenfolge.

Hier kommts drauf an mit was du arbeitest ...
Arbeitest du mit signalen, brauchst du eigentlich nur nen sammler, der die finished signale aller threads sammelt, und dann selber nen signal emitted. nun musst nur noch den eigentlichen Close Pfad beibringen, das er auf das alle-finished signal wartet.

Arbeitest du mit den wait funktionen, gehts etwas komplizierter ...

DU kannst hilfsthreads zum beenden erzeugen ....
Du kannst auf BS runter statt mit QThreads arbeiten. Unter windows kannst z.b. mit WaitForMultipleObjects ... auf eine Menge an Threads warten ....

Schau dir mal QThreadPool an, das eigentlich fuer gemacht, wenn man mehrere gleichartige threads fuer eine aufgabe braucht ... vielleicht bietet die was ...

Ciao ...
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

Beitrag von Willi2793 »

Zuerst mal vielen Dank für die Anregungen.
RHBaum hat geschrieben:Du kannst threads auch länger laufen lassen, als wie ihr erzeugerthread existiert. Du musst nur sicherstellen, das der laenger laufende Thread voellig autark laufen kann, und nicht etwa auf ressourcen zugreift, die mit dem erzeugerthread ins nirvana verschwinden.
Das wollte ich eigentlich vermeiden. Ich hätte gerne das, wenn main() beendet ist auch alles Andere beendet ist.
RHBaum hat geschrieben:Assynchron beenden, bedeutet in deinem Fall, du schiesst das beenden an alle Threads raus, und dann wartest, bis sich alle beendet haben, unabhaengig der reihenfolge.

Hier kommts drauf an mit was du arbeitest ...
Arbeitest du mit signalen, brauchst du eigentlich nur nen sammler, der die finished signale aller threads sammelt, und dann selber nen signal emitted. nun musst nur noch den eigentlichen Close Pfad beibringen, das er auf das alle-finished signal wartet.
Genau das ist es was ich mache/machen will. In meinem WorkerThread feuere ich in dem Slot der aufgerufen wird wenn QCoreApplication das aboutToQuit() absetzt ein Signal an die Sub-Threads um diese zu beenden. Diese wiederum schicken ein finished welche ich sammele. Aber irgendwie habe ich nun Probleme im Slot der bei aboutToQuit() aufgerufen wird erst dann zu beenden wenn alle finished gesammelt sind.
RHBaum hat geschrieben:Schau dir mal QThreadPool an, das eigentlich fuer gemacht, wenn man mehrere gleichartige threads fuer eine aufgabe braucht ... vielleicht bietet die was ...

Ciao ...
Das erscheint mir nicht das was ich suche.

Ich hoffe ich habe mich verständlich ausgedrückt.
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Wenn main() vorbei ist sind auch alle Threads tot. main() ist kein Thread sondern der Einsprungpunkt in dein Programm. Aber: startest du in einem THreads neue THreads, können die "Kind"-Threads den Elternthread überleben. Das meine RHBaum.

aboutToQuit ist mMn. zu spät. Du triggest doch selber das Beenden. Im main-Widget das closeEVent() halte ich für besser geeignet. Dort kannst du das Beenden der Threads anstoßen und das übergebene event mit ignore() ignorieren. Um zu erfahren, wann jetzt alle Threads beendet wurden, ist eine Hilfsklasse ganz hilfreich, die die einzelnen THreads terminiert und die interessierte Umgebung bei Erfolg auch informiert.

Und warum ist QThreadPool nix für dich?
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

Beitrag von Willi2793 »

franzf hat geschrieben:Wenn main() vorbei ist sind auch alle Threads tot. main() ist kein Thread sondern der Einsprungpunkt in dein Programm. Aber: startest du in einem THreads neue THreads, können die "Kind"-Threads den Elternthread überleben. Das meine RHBaum.
Klar, das hatte ich verstanden. Das würde ich aber gerne vermeiden.
franzf hat geschrieben:aboutToQuit ist mMn. zu spät. Du triggest doch selber das Beenden. Im main-Widget das closeEVent() halte ich für besser geeignet. Dort kannst du das Beenden der Threads anstoßen und das übergebene event mit ignore() ignorieren. Um zu erfahren, wann jetzt alle Threads beendet wurden, ist eine Hilfsklasse ganz hilfreich, die die einzelnen THreads terminiert und die interessierte Umgebung bei Erfolg auch informiert.
Das hatte ich auch zuerst so. Aber das ganze ist eine Art Server-Anwendung die unter Umständen auch völlig ohne Oberfläche laufen soll.

Und das mit der hilfsklasse habe ich im Prinzip ja. Die Hilfsklasse hat einen Slot der bei aboutToQuit() aufgerufen wird und alle Sub-Threads benachrichtigt sich zu beenden. Das funktioniert auch wunderbar. Auch das Sammeln der finished-Signale der Sub-Threads. Aber diese Signale kommen natürlich in einem anderen Thread an und nun stehe ich vor dem problem das der Slot, der die Sub-Threads beendet beendet wird bevor diese beendet sind und damit auch aboutToQuit() beendet wird. Das führt dazu das main() zu früh beendet ist.
franzf hat geschrieben:Und warum ist QThreadPool nix für dich?
Ich habe das so verstanden das dies ein Hilfsmittel ist um Threads die öfter gestartet werden und kurzlebig sind zu verwalten. Hier liegt aber ein Szenario vor das die Sub-Threads gleich am Anfang gestartet werden und genauso lange leben sollen wie das ganze Programm.
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Wenn main() vorbei ist sind auch alle Threads tot.
Nenene, ich meinte das schon anders :-)
deine main ist deine einstiegsfunktion klar, dafuer wird dir vom BS fuer den Prozess, der MainThread generiert.
Wenn die main durchgelaufen ist, wird der MainThread irgendwann auch terminiert, klar.

Lauft deine main aber durch, und es sind noch vom mainThread erzeugte Prozesse aktiv, beendet sich der Prozess ned. Deine main ist dann aber schon vorbei. Ergo, auch nach der main koennen noch Threads aktiv sein.

Dein Prozess beendet sich dann nicht, und da deine main nicht mehr lauft, hasst von da aus auch keine kontrolle mehr. der Mainthread ist auch quasi abgelaufen, der wird auch sicher nicht mehr auf signale reagieren.
Alles in allen eine unschoene Situation, deshalb vermeidet man sie eigentlich. Aber technisch möglich isses schon ... unter windows funktioniert das zumindest so ...

@Willi2793
Arbeitest du mit Qt-Signalen ist Dein Hauptproblem, dass du deinen Mainthread nicht blockieren kannst. Weil blockierst den, werden die Signale von den anderen Threads nicht mehr an deinen Slot ausgeliefert (Siehe Signale / Slots und die MsgLoop).
Das heisst du musst auf das Close Signal reagieren, und da aber die steuerung zurueck an die loop geben.
Also wenn du in der Qapp das abfaengst, einfach das mainfenster schliessen, dann kann der user nix mehr anstossen, und Du kannst gemuetlich auf dein "NunSindAlleThreadsTot" Signal warten. Der user kriegt eh nix mit, weil er sieht nix mehr, ausser im taskman wuerd er noch was sehen.
Haendelst das aber In ner Fensterklasse ab (MainWindow). wuerd ich alle Interaktionsmöglichkeiten vom user abklemmen, alle Fenster zu machen, alles auf disabled setzen ... und dann warten. Sieht er zwar noch nen doofes Fenster, mit dem kann er nix anfangen, irgendwann verschwindets aber mal ...

Mit warten mein ich in dem Fall, den Slot durchlaufen lassen, das er in die Loop zurueckkehr, das close irgendwie auf ignoriert setzen, und dann im slot zum alle threads beendet Signal, die letzten aufraeumarbeiten durchfuehren und die App runterfahren (main auslaufen lassen, exit aufrufen)

Ciao ...
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

Beitrag von Willi2793 »

So, ich habe jetzt mal ein kleines Diagramm erstellt wie ich mir das vorstelle bzw. jetzt auch realisiert habe. Es dient nur zur Veranschaulichung.

In Main wird also der WorkerThread erstellt der wiederum viele Sub-Threads erstellt. Von mai wird aboutToQuit() von QCoreApplication mit endWork() des WorkerThread connected. der wiederum schmeißt dann das endSubThreads()-Signal welches mit dem endWork() der Sub-Threads connected ist.

Die Sub-Threads beenden ihre Arbeit und sich dann selbst und schmeißen am Ende das finished()-Signal welches mit dem subthreadFinished()-Slot des Workerthtread connected ist. Hier wird jetzt überwacht ob alle Sub-Threads beendet sind und wenn dann der Letzte das Ende gemeldet hat wird er sich selbst beenden und seinerseits das finished() auslösen.

Und genau das sollte jetzt bewirken das es nach dem aboutToQuit() weiter geht.

Und wie vorhin schon gesagt: Das Ganze sollte auch ohne GUI funktionieren (Wie ich das aboutToQuit() auslöse ist hier erstmal zweitrangig).

:) Ich hoffe es wird jetzt klarer.
Dateianhänge
WorkerThread.png
WorkerThread.png (13.61 KiB) 7266 mal betrachtet
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Ok, keine GUI, aber Signale, also du hasst ne MainApp (Abgeleitet von QApplikation).

Du hassst doch im WOrkerthread hoffentlich ne Verwaltung deiner subthreads ? richtig ?

wenn sich so nen subthred beendet (subthreadfinished SLOT) dann nimmst den doch aus der liste raus. und wenns der letzte ist ... schickst das endSubThreads signal ... auch ok.

Du hasst ne schoene reinrassig assynchrone umgebung. Dein Problem iss eher das du die assynchronitaet synchron machen willst.
Du willst nen Aussprung aus der Mainloop (aboutToQuit()) blockieren lassen, Ohne dass du die weitere Abarbeitung des der Mainloop blockieren lassen willst.

Das geht nur ueber "Tricks" .... IMHO aber schmutzig

- Fang das aboutToQuit in deiner eigenen App (von QApplication abgeleitet) ab.
- Fang das finished signal vom deinem Workerthread ab. und setz da ne Statusvariable um, sagen wir bool mWorkerThreadsFinished auf true ...

in deinem Slot der auf aboutToQuit triggert machst dann sowas:

Code: Alles auswählen


    /// deinem WOrkerthread die Info schicken das er sich beenden soll 
    /// am besten mit eigenen Signal 
    emit shutdownThreads();
    /// nun warten bis das signal ankommt ohne die eventloop blockieren -> schmutzig 
    while(!mWorkerThreadsFinished)
    {
         processEvents(); /// die Eventloop manuell anfeuern, also eventloop in eventloop ausführen 
    }
Besser:
- aboutToQuit() abfangen
- Das thread finishen anstossen
- Event auf ignored setzen
- in die Mainloop zuruecklassen
- Auf das Finished vom Worker warten (SLOT)
- wenn der slot gefeuert, Application mit CloseEvent noch mal runterfahren lassen, diesmal aber aboutToQuit nicht ignoriert durchlassen.

Ciao
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

warte, ich lese grad:
This signal is emitted when the application is about to quit the main event loop, e.g. when the event loop level drops to zero. This may happen either after a call to quit() from inside the application or when the users shuts down the entire desktop session.
The signal is particularly useful if your application has to do some last-second cleanup. Note that no user interaction is possible in this state.
Die Frage ist hier, iss bei aboutToQuit die eventloop ueberhaupt noch funktional ?
wenn nein, muesstest du weiter oben ansetzen.
und zwar das CloseEvent abfangen indem
virtual bool event ( QEvent * e )
überschreibst ....

Ciao ...
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

Beitrag von Willi2793 »

Vielen Dank für Deine Anregungen. Die Diskussion hat mir dann auch den Kopf freigeblasen und die Idee gebracht. Anstatt auf aboutToQuit() zu reagieren wenn ich die Anwendung benden will, rufe ich zuerst das beenden des WorkerThreads auf und das quit() der Q(Core)Application connecte ich mit dem finished() des WorkerThreads. Eigentlich ganz einfach, aber wenn man mal nur Bäume sieht ist der Wald weg :D

Also nochmal: vielen Dank!
Willi2793
Beiträge: 147
Registriert: 29. September 2008 10:59
Kontaktdaten:

Beitrag von Willi2793 »

Zusatz: Natürlich habe ich eine Verwaltung der SubThreads :wink:
Antworten