Laden...

BackgroundWorker und modale Dialoge

Erstellt von FSA9Code vor 5 Jahren Letzter Beitrag vor 5 Jahren 2.632 Views
FSA9Code Themenstarter:in
2 Beiträge seit 2018
vor 5 Jahren
BackgroundWorker und modale Dialoge

Hallo,

ich arbeite mich gerade in WPF ein und erstelle dazu ein paar kleine Übungsprojekte. Aktuelles Problem ist die Animation (Indeterminate) einer ProgressBar in Verbindung mit modalen Dialogen und MessageBox.

Das Beispielprogramm ist ein simpler Texteditor. Es kann nur eine Datei gleichzeitig bearbeitet werden. Nehmen wir folgenden (vereinfachten) Workflow zum Öffnen einer Textdatei an:

  1. Speichern-Dialog falls Änderungen
  2. Datei speichern
  3. Öffnen-Dialog anzeigen
  4. Datei öffnen
  5. Datei in UI anzeigen

Nach der Empfehlung sollten Dialoge im UI-Thread angezeigt werden und längere Aktionen, wie Datei schreiben und lesen, im BackgroundWorker.

Erster Lösungsversuch: im Prinzip läuft alles in der DoWork ab. Funktioniert auf den ersten Blick einwandfrei und benötigt kaum zusätzlichen Code. Das ist leider nicht sauber und könnte später zu Problemen führen (kann ich mit meinem Wissensstand schlecht abschätzen).

Zweiter Lösungsversuch: Trennung der Long-Time-Operations von der UI:

  1. Im UI-Thread Speichern-Dialog anzeigen
  2. Progressbar einschalten
  3. Datei speichern über BackgroundWorker DoWork
  4. Im RunWorkerCompleted:
    • Datei-Öffnen-Dialog angezeigen
    • BackgroundWorker erneut starten.
  5. Datei öffnen über BackgroundWorker DoWork
  6. Im RunWorkerCompleted:
    • Progressbar deaktivieren
    • Datei in der UI anzeigen

Funktioniert auch, benötigt jedoch durchgeschleifte Steuerungsinformationen und wesentlich mehr Code. Fühlt sich auch nicht optimal an.

Und jetzt weiß ich auch nicht. Langsam beschleicht mich das Gefühl, ich habe Etwas Wichtiges bei dem Thema übersehen und mein Ansatz ist komplett falsch.

Wie würdet ihr das lösen?

4.938 Beiträge seit 2008
vor 5 Jahren

Hallo und willkommen,

der BackgroundWorker ist mehr dazu gedacht, Aufgaben auszuführen, die gleichzeitig zur UI ausgeführt werden sollen. Da du jedoch während des Speicherns quasi die UI-Interaktionen unterbrechen möchtest (bis auf die ProgressBar), benutze dazu besser die asynchrone Programmierung mittels der Sprachelemente async/await (s.a. GUI im Hintergrund aktualisieren: Task.Run vs. async/await).
Dadurch wird der Code auch viel eleganter, d.h. du kannst einfach die 5 Punkte sequentiell in einer Methode ausführen lassen.

Hier noch 2 Tutorials zum Thema async/await:
https://kerry.lothrop.de/async-schluesselwort/ sowie https://kerry.lothrop.de/await-schluesselwort/
http://codingsonata.com/your-ultimate-async-await-tutorial-in-csharp/

16.830 Beiträge seit 2008
vor 5 Jahren

Man kann keine UI-Aktion "unterbrechen", sondern würde diese ja blockieren, sofern ich das jetzt wortwörtlich übernehmen würde.

Alle Aktionen, die länger als 200ms benötigen, müssen ausgelagert werden.
Dabei kann man async/await verwendet (vor allem für IO), oder man muss das ganze in einen Thread (man kann auch einen Task verwenden, der im Hintergrund einen Thread verwendet) auslagern, sofern es sich um Berechnungsoperationen handelt.
Ansonsten kommt Windows schnell mit seinem Not Responding auf dem Desktop; bzw. auf mobilen Geräten würde die Anwendung vom OS abgeschossen werden.
Daher gibt es auf mobilen Plattformen eigentlich auch nur asynchrone Operationen in den entsprechenden SDKs und Runtimes.

Fortschritte werden dann zB durch Events an die UI mitgeteilt.

PS: Kerry weiß was er schreibt, absolut lesenswert! Könnte man kaum besser erklären.

P
1.090 Beiträge seit 2011
vor 5 Jahren

Und jetzt weiß ich auch nicht. Langsam beschleicht mich das Gefühl, ich habe Etwas Wichtiges bei dem Thema übersehen und mein Ansatz ist komplett falsch.

Der Zentrale Punkt ist, wie es für den Benutzer herüber kommt. Wenn die Datei in 20ms gespeichert ist, brauchst du keine asynchrone Programmieren und solltest sie auch in den meisten Fällen Vermeiden (Es ist einfacher zu Programmieren).
Wenn das Speichern einer Datei 1 Minute dauert sollte man den Benutzer anzeigen, das was geschieht (Was komplizierter ist).

Ich finde gerade den Artikel nicht. Aber wenn ich es richtig im Kopf habe.

Werden Vorgänge < 100ms als "relative" flüssig Endfunden.
Von 100ms bis 200ms als "stockend".
Über 200ms als Hakend. (Es kommt da auch immer auf die Aktion an, wenn ich eine Maus bewege, möchte ich das sie sich direkt bewegt. Speicher ich eine Datei ist es Ok kurz zu warten.)

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

16.830 Beiträge seit 2008
vor 5 Jahren

Der Zentrale Punkt ist, wie es für den Benutzer herüber kommt. Wenn die Datei in 20ms gespeichert ist, brauchst du keine asynchrone Programmieren und solltest sie auch in den meisten Fällen Vermeiden (Es ist einfacher zu Programmieren).

Es gibt über viele Sprachen und Technologien hinweg (aktuell hyped das Thema ja in JavaScript sehr): wenn es eine asynchrone Schnittstelle gibt: verwende sie.

Es gibt zB. vom Dateisystem nie die Garantie, dass das Speichern nur 20ms benötigt.
Wenn die Platte ausgelastet ist, werden schnell Sekunden draus.

Als asynchrone Programmierung nur via Callbacks und Promises funktioniert hat; hier versteh ich den Ansatz "einfacher zu programmieren".
Aber async/await zu vermeiden, weil es "nicht einfach" ist: no. Einfacher geht es ja wirklich nicht mehr.

P
1.090 Beiträge seit 2011
vor 5 Jahren

Also wenn die CPU zu 100% ausgelastet ist bringt mir async auch nichts. Ich denke das eine Betrachtung von knappen System Ressourcen für eine Verallgemeinerung, nicht wirklich sinnvoll ist. Wenn es irgendwo einen Flaschenhals gibt muss ich natürlich schauen, das ich ihn bei meiner Programmierung berücksichtige. Aber das ist ein anderes Thema.

Auch wenn async mittlerweile leichter zu Programmieren ist, heißt es nicht das ich es überall Einsätzen soll.

Einmal entsteht durch den Thread wechsle eine Overhead statt. Wenn ich einfach 2 Zahlen addiere ist der Overhead größer als die eigentliche Operation. Grundlegend lohnt es sich also nicht Operationen auszulagern bei dehnen der Overhead größer ist als die eigentliche Operation.

Dann muss ich auch immer drauf achten das keine Raceconditions auftreten. Wenn das Programm noch in eine Datei schreibt. Kann ich keine Änderungen in der Datei speichern. Das muss ich abfangen. Zusätzlich brauch der User da auch ein Feedback wie der aktuelle Stand ist (z.B. Vorschritts anzeige).

Zusätzlich kommt kommt hinzu das async Funktionen meist auch schwerer zu Debuggen sind.

Ich persönlich halten den Ansatz für richtig asyc zu Benutzten wenn ich es muss. Aber nicht einfach weil ich es kann. (KISS, YAGNI).

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

4.938 Beiträge seit 2008
vor 5 Jahren

Palin, da findet kein Threadwechsel bei async statt! Lies auch du mal GUI im Hintergrund aktualisieren: Task.Run vs. async/await.

Sorry, aber dein ganzer Beitrag liest sich so, als ob du asynchrone Programmierung noch nicht richtig verstanden hast.

T
2.223 Beiträge seit 2008
vor 5 Jahren

@Th69
Hab ich mir mal angeschaut und bin da etwas überrascht.
Ich dachte eigentlich, da ein Task zum Einsatz kommt bei async/await, würde etwas ähnlich wie bei der Task Factory dann irgendwann den Task im Background in einem eigenen Thread laufen zu lassen.

Hier macht await/async dann auch absolut Sinn, da der eigentliche Thread der auf den Task wartet den Task auch ausführt.
Somit spart man sich zum einen den extra Thread + nutzt den wartenden Thread zum abarbeiten und entgeht damit auch den Raceconditions.
Sehr genialer Ansatz.
Bisher dachte ich einfach, dass es eine syntaktische Änderung ist und sonst ganz normal wie Tasks funktioniert.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

16.830 Beiträge seit 2008
vor 5 Jahren

Also ich muss da Th69 zustimmen, der Beitrag von Dir Palin suggeriert, dass Du async/await nicht verstanden hast.
Zumindest zeigt das dieser Satz extrem:

Wenn ich einfach 2 Zahlen addiere ist der Overhead größer als die eigentliche Operation.

Asynchrone Programmierung verwendet man vor allem dann, wenn man externe Schnittstellen oder Operationen verwendet, dessen Antwortgeschwindigkeit bzw. Zeit man nicht beeinflussen kann, für den Programmfluss aber notwendig ist.

Natürlich fällt hier ein einfaches Addieren zweiter Zahlen überhaupt nicht in den Fokus; ich hab doch hier überhaupt keine externe Schnittstelle, auf die ich irgendwie potentiell warten müsste. Wieso denn dann beim einfachen Addieren irgendwas asynchron umsetzen? Das Beispiel hinkt von Vorn bis Hinten.
Was zeigt: Palin, hier solltest Du nochmal etwas Lektüre zu Dir nehmen.
Operationen gegen die Festplatte, gegen APIs bzw. einfach gegen Schnittstellen fallen aber ausnahmslos immer in diesen Fokus.

Der einzige Overhead hier ist prinzipiell auch nur die StateMachine; nicht irgendein Thread-Wechsel, den es nicht gibt.
Die StateMachine muss aber initialisiert werden, weshalb das einen deutlichen Impact auf den ersten Call der asynchronen Methode mit sich bringt.
Das alles rechtfertigt es aber nicht, async/await nicht zu verwenden.

Auch hat eine 100% CPU Auslastung prinzipiell mal nichts mit asynchroner Programmierung zutun oder wäre ein Grund, dies nicht zu tun.
Genausowenig irgendein KISS oder YAGNI. Sorry, Du hast es einfach nicht verstanden, worum es hier geht.
Async Programmierung ist nicht abhängig von irgendwelchen Ressourcen im Sinne von Last; jedenfalls nicht direkt damit gekoppelt.

Es gibt von Microsoft sogar eine viel striktere Empfehlung: alle Methoden, die potentiell länger brauchen können als 50ms, sollten eine asynchrone Schnittstelle aufweisen.
Alle.

Ich persönlich halten den Ansatz für richtig asyc zu Benutzten wenn ich es muss. Aber nicht einfach weil ich es kann. (KISS, YAGNI).

Mit der Meinung im Sinne von "ich bin bockig und hab keine Lust dazu" stehst Du nur sehr einsam da im Vergleich der führenden Technologien und Sprachdesignern.
Auch sieht es eben so aus, dass Du mit dieser Grundhaltung auf Plattformen wie iOS, Android und Co wenig Mitleid ernten wirst, weil das System einfach Deine App abschießt, wenn Du Dich nicht an die Empfehlung hälst. Streng genommen fällt das also unter Deine Aussage "muss".

Asynchrone Programmierung gehört auch einfach in den Grundkontext des Defensives Programmieren

P
1.090 Beiträge seit 2011
vor 5 Jahren

Also ich hab das Beispiel aus dem Beitrag (In dem Thread geht es um eine Andere Frage)
jetzt einfach mal

Synchron ausgeführt

duration 1 : 00:00:00.0002001
duration 2 : 00:00:00.0000011
duration 3 : 00:00:00.0000003
duration 4 : 00:00:00.0000007
duration 5 : 00:00:00.0000007
duration 6 : 00:00:00.0000003

und Asynchron.

duration 1 : 00:00:00.0310582
duration 2 : 00:00:00.0001965
duration 3 : 00:00:00.0001148
duration 4 : 00:00:00.0001227
duration 5 : 00:00:00.0000896
duration 6 : 00:00:00.0001184

Der 1. Asynchrone Aufruf dauert länger als alle 6 Synchronen Aufrufe zusammen.
Ich sehe hier absolut keinen Grund es Asynchron auszuführen.

Das hat nicht mit Bockig zu tun. Das sind Zahlen.

Ich hab da einen einfachen Grundsatz, halte dich an Standards und mache es so einfach wie Möglich. (Wenn Auszubildende den Code Verstehen hab ich gute Arbeit geleistet, senkt auch deutlich die Kosten von Software, da der Quellcode von den meisten Gefleht werden kann.)
Und wenn ich es komplizierter machen will, muss ich einfach einen Mehrwert belegen können.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

16.830 Beiträge seit 2008
vor 5 Jahren

Les mein Beitrag durch - evtl. verstehst Du dann worum es geht.
Mit dem Beispiel hier machst Du Dir keinen Gefallen, denn Du beweist damit erneut, dass Du nicht verstehst, worum es geht - oder Du bist einfach stur und bockig.

Ansonsten bleibt nichts anderes als das Fazit zu sehen, dass Du async/await nicht verstanden hast - oder evtl. einfach auch nicht verstehen willst.

Auch Dein "Grundsatz" ist völlig aus der Luft gegriffen.
Völlig.

P
1.090 Beiträge seit 2011
vor 5 Jahren

Abt wie so sollte sich jemand die mühe machen dich zu erstehen. Wenn du dir keine Mühe gibst ihn zu Verstehen?

Der Grundsatz begründet sich auf ein paar einfache Grundlagen. KISS, YAGNI und wenn man mochte auf Coding conventions.

Grundlegend sag ich nicht anderes als."Weise einen Nutzen nach, wenn du es kannst ist es OK es zu verwenden. Wenn nicht, lass es sein."

Mir erschließt sich einfach nicht der Sinn, darin etwas komplizierter zu machen als man muss.

p.s.
Abt nach etlichen Diskussionen mit dir und deren Verlauf. Bin ich an der Stelle, bei deinen Antworten raus.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

16.830 Beiträge seit 2008
vor 5 Jahren

Du musst nicht mich verstehen; damit werde ich durch den Tag kommen - Du musst das Prinzip und das Ziel von asynchroner Programmierung verstehen. Dafür solltest Du Dir die Zeit nehmen und die Mühe opfern - besonders wenn Du hier im Forum aktiv Leuten helfen willst....
Du schießt hier ein Eigentor nach dem anderen, reitest Dich immer mehr in den Schlamassel , weil Du hier "Grundsätze" und "Beispiele" raushaust, die eine Offenbarung sind, dass Du es nicht verstanden hast, was das Ziel ist.
Und dann kommst Du auch noch mit KISS und YAGNI....die damit absolut nichts am Hut haben und rechtfertigst auch noch damit Deine Aussagen 😭

Wenn Dir der Sinn nicht erscheint, dann hast Du das Grundkonzept von async/await einfach nicht verstanden. Und wenn das Verständnis fehlt, dann erkennt man natürlich auch den Nutzen nicht.
Es ist okay, dass Du raus bist - damit sind hoffentlich auch die verfehlten Beispiele dann vom Tisch; aber nutz die Zeit Dir das Konzept von asynchroner Programmierung anzuschauen.

.. und die Diskussionen hast Du ja nicht nur mit mir. Und der Hinweis, dass Du es nicht verstanden hast, kam ja initial nicht von mir 😉
Alles Hinweise gewesen, dass Du evtl. eine Pause vor einer Antwort hättest einlegen können, und 15 Minuten sich mit async/await und der asynchronen Programmierung zu beschäftigen.
So war das ein ziemlicher Offenbarungseid und eine Vielzahl von Eigentoren.
Ich halte leider oft jedoch nicht den Mund, wenn man hier Hilfesuchende gegen die Wand fahren lässt.

Schönen Sonntag.

FSA9Code Themenstarter:in
2 Beiträge seit 2018
vor 5 Jahren

Vielen Dank an TH69 und Abt für die Tipps, besonders für die Links!

Mein Problem konnte damit gelöst werden, funktioniert jetzt wie gewünscht. Der Code ist wesentlich übersichtlicher und auch nicht viel komplexer als bei synchroner Implementation.