Standardlösung bei zeitintensiven Aktionen in einer GUI-Anwendung
-
Wieso ist hustbaer eigentlich immer so unfreundlich?
Ich würde Threads vermeiden wo es nur geht. Kaum kommen Threads zu einer Anwendung dazu, betrittst du ein Reich voller Schmerzen.
-
meistens, wie auch hier, hat hustbaer aber Recht mit dem, was er sagt.
-
anmerker schrieb:
Wieso ist hustbaer eigentlich immer so unfreundlich?
In diesem Fall weil mich das "
", in Kombination mit einer Formulierung die verdammt nach "Windows-Bashing" klingt, genervt hat. Und der IMO falsche Ratschlag. Er. Ratschläge.
Beim Thema Threads etwas vorzuschlagen, was alles noch (unnötigerweise) verkompliziert, ist IMO böööööse.
Ich würde Threads vermeiden wo es nur geht. Kaum kommen Threads zu einer Anwendung dazu, betrittst du ein Reich voller Schmerzen.
Das sehe ich auch so. Allerdings wenn man schonmal Threads verwendet (weil es nicht anders geht, weil man es nicht besser weiss, warum auch immer), dann macht es IMO wirklich keinen Sinn, die Sache noch mehr als nötig zu verkomplizieren.
Und für Aktionen auf die der User warten muss extra einen Thread-Pool einrichten, zahlt sich IMO überhaupt garnicht aus - begründung siehe oben.Wenn man schon einen Thread-Pool hat, und der einfachste Weg zu einem Thread zu kommen der ist, den Thread-Pool zu verwenden, dann natürlich den einfacheren Weg wählen!
Sich zu überlegen, ob man an einer bestimmten Stelle wirklich einen Worker-Thread braucht, ist dagegen zwar an sich ein guter Ratschlag, aber die Begründung ist IMO total daneben. Sowas führt nur zu noch mehr Programmierern die mit sinnlosen Optimierungen ihr Programm verunstalten.
EDIT:
betrittst du ein Reich voller Schmerzen.
Nette Formulierung BTW
EDIT2:
Ich weiss nicht wie viele das schon bemerkt haben, aber beim Thema Threads ärgere ich mich ziemlich schnell mal. Liegt einfach daran dass a) so viel Mist zu dem Thema verzapft wird, es b) so viele Leute gibt die so viele Fehler diesbezüglich machen, und c) es zu Fehlern führt, die man oft kaum finden kann.
Ja, ich weiss, bringt nix, ist bloss meine gute Stimmung die ich mir damit versaue, aber ist halt so. What shalls, niemand ist Perfekt.
-
naja, faktisch gibts zu dem problem nur zwei möglichkeiten:
a) im selben thread arbeiten lassen und dabei regelmäßig gui events verarbeiten (das kann natürlich auch abenteuerlich werden)
oder
b) die arbeit in einen anderen thread oder prozess auslagern.Ich denke das Problem bei Threads ist, dass jeder relativ schnell das Prinzip versteht und dann meint "so schlimm ist das doch gar nicht". Zumal man die Probleme selten auf Anhieb serviert bekommt.
(Punkt a ist nicht zu verwechseln mit nimlots nr 1. Die GUI einfrieren lassen empfinde ich als inakzeptabel)
-
Nimlot schrieb:
2.) Ich starte einen neuen Thread für die zeitintensive Operation.
Ist natürlich toll für den Benutzer, weil die GUI weiterhin funktioniert und ich ihm die Möglichkeit geben kann, den Prozess abzubrechen bzw. ihn über eine ProgressBar ect. auf den aktuellen Stand zu halten. Für mich macht es die Sache allerdings kompliziert: Wenn die GUI-Objekte nicht Thread-Sicher sind, muss ich mich um die Synchronisation kümmern bzw. in C# Delegates verwenden ect. Außerdem muss ich mir Gedanken machen, welche GUI-Elemente ich sperre, damit mir eine parallel gestartete Aktion nichts durcheinander bringt u.s.w.Irgendwie habe ich auch das Gefühl, dass viele große und bekannte Anwendungen mit diesem Problem nicht richtig zurecht kommen (klickt mal beim Windows-Explorer auf Netzwerkverbindungen...)
Thread starten ist das richtige, wie auch von hustbaer vorgeschlagen.
Und ja du hast recht, das ist unter Umständen nicht so trivial und ja es gibt viele Anwendungen die so Sachen verhunzen.
Wenn du dir über solche Dinge schon vorm Programmieren deiner GUI Gedanken machst (sprich wo braucht man Threads etc) sparst du dir letztlich Zeit.
-
Unter OS/2 gab es eine einfache Richtlinie: Alles was länger als 10 ms dauert/dauern könnte gehört in einen hintergrund Thread (OS/2 hatte nur eine einzige Message-Queue, dort hing nicht nur die eigene Anwendung sondern sämtliche GUI-Anwendungen).
Diese Regel habe ich auch unter Windows beibehalten und bin immer gut damit gefahren.
-
hustbaer schrieb:
"Windows-Bashing"
Von Artchi? Wo denkst du hin?
DrGreenthumb schrieb:
naja, faktisch gibts zu dem problem nur zwei möglichkeiten:
a) im selben thread arbeiten lassen und dabei regelmäßig gui events verarbeiten (das kann natürlich auch abenteuerlich werden)
[...]
(Punkt a ist nicht zu verwechseln mit nimlots nr 1. Die GUI einfrieren lassen empfinde ich als inakzeptabel)Du denkst an so etwas wie das gelegentliche Aufrufen von
Application->ProcessMessages()
, also das rekursive Aufrufen der Message-Pump? Das ist meiner Erfahrungen nach von allen Lösungen die schlechteste: es ist ähnlich schwierig beherrschbar wie das Multithreading, außerdem ein Workaround, und es schafft eine überaus lästige Abhängigkeit. Da lasse ich meine Benutzer lieber ein wenig auf die Sanduhr schauen.Die korrekte Lösung ist natürlich ein Hintergrund-Thread sowie, wenn sinnvoll und möglich, ein "Abbrechen"-Knopf.
-
audacia schrieb:
Du denkst an so etwas wie das gelegentliche Aufrufen von
Application->ProcessMessages()
, also das rekursive Aufrufen der Message-Pump? Das ist meiner Erfahrungen nach von allen Lösungen die schlechteste: es ist ähnlich schwierig beherrschbar wie das Multithreading, außerdem ein Workaround, und es schafft eine überaus lästige Abhängigkeit. Da lasse ich meine Benutzer lieber ein wenig auf die Sanduhr schauen.ohne Abbruchmöglichkeit? Wenn man nur eine längere Aktion macht wo ein paar Labels geupdatet werden und ausser Abbrechen-Button keine Interaktion möglich ist, kann man das doch ruhig machen.
Sollte man auf jeden Fall im Hinterkopf behalten. Einfach immer blind mit Threads anfangen ist, bei dem entstehenden Aufwand, sicher auch nicht das wahre.
-
Also ich finde "ProcessMessages" auch ziemlich unsauber...
Vor allem hab' ich gesehen was Leute damit für Unsinn anstellen, und wie schwer diese Probleme dann festzunageln bzw. zu fixen sind.
Es gibt ja z.B. auch sowas wie Timer-Callbacks. Die laufen dann auch munter in "ProcessMessages" - etwas woran viele Leute nicht denken.Da pack ich den ganzen Code lieber in einen Thread, und die Sache ist erledigt.
Ich denke man muss bei beiden Varianten wissen was man tut.
Und man kann bei beiden Varianten Fehler machen, die schwer zu finden sind.Und das "was ist OK, was ist nicht OK" ist denke ich auch bei beiden Varianten ziemlich schwer zu erklären, so dass es jmd. der es nicht bereits weiss auch versteht.
-
ich habe auch eine mitlerweile mittelgrosse app "unter mir" - bei denen werden aufgaben mittels com auf den server abgearbeitet
das dauert von 10 sek bis zu N stundenfuer diesen fall habe ich mir auch was generisches ueberlegt
ich habe eins abstracted ApiThread klasse erstellt
und aktionen werden dann in den aufgaben eingeteilt
UserReader, oder DocumentLanguagesReader
das erbt von ApiThread und implementiert die funktionalitaet und schickt events wenn der progress sich aendert, schickt ein event wenn er fertig ist mit einem return, und provided ein cancelin der applikation dann kann man es dann einfach direkt verwenden, dh man aboniert das progresschanged und finished, und startet es
dann wird ein please wait modal angezeigt welches auch ein cancel button hat und alles drum herum
das cancel wird dann bei klick auf das thread objekt weiter delegiertetwas komplizierter wurde es da ich verschiedene aufgaben immer wieder brauch - aber abhaendgigkeiten voneinander entstehen - dh die ergebnisse vom UserReader und DocumentLanguagesReader wird fuer ein dritten thread gebraucht
man koennte sich dann so eine kette aufbauen - aber ich wollte es generischer machen - drum habe ich dann ein ThreadQueue gebastelt
da drueckt man die aktionen rein die nacheinander aufgerufen werden sollen und startet es einfach
es schickt auch wieder progress aktionen und wenn alles fertig ist
ergebnisse werden zusammen gesammelt und die parameter sind immer wie vorgegebenwenn man diese architektur einmal aufgebaut hat ist es sehr leicht eine weitere grosse aktion hinzu zu fuegen und man hat nur ein paar kleine zeilen des aufrufs
am ende gibt es in der applikation kein zeitpunkt mehr wo die ui auch nur ansatzweise einfriert
(entwickelt in c# mit wpf und dem mvvm pattern)
-
Das Pattern zur Lösung solcher Probleme nennt sich "Active Object".