Einfrieren der Windows Forms Anwendung verhindern

  • C#

Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Einfrieren der Windows Forms Anwendung verhindern

    Guten Abend,


    Ich habe spaßeshalber eine Windows Forms Anwendung programmiert, in der via Button-Klick eine sehr aufwändige rekursive Funktion (Fibunacci) berechnet wird, das ist nicht weiter spannend. Interessant ist hingegen, dass die Anwendung einfriert sobald ich einen großen Wert eingebe (die rekrursive Variante wurde genau deshalb verwendet, um dieses Verhalten zu reproduzieren). Meine Frage ist, wie kann man es erreichen, dass die Forms Anwendung noch immer benutzbar ist und die Berechnung im Hintergrund abläuft?

    Hier einmal mein Quelltext:

    C#-Quellcode

    1. using System;
    2. using System.Collections.Generic;
    3. using System.ComponentModel;
    4. using System.Data;
    5. using System.Drawing;
    6. using System.Linq;
    7. using System.Text;
    8. using System.Threading.Tasks;
    9. using System.Windows.Forms;
    10. namespace WindowsFormsApp2
    11. {
    12. public partial class Form1 : Form
    13. {
    14. public Form1()
    15. {
    16. InitializeComponent();
    17. }
    18. private void button1_Click(object sender, EventArgs e)
    19. {
    20. for(int i = 0; i < 10; i++)
    21. {
    22. MessageBox.Show("" + fibu(i), "W");
    23. }
    24. }
    25. public static int fibu(int n)
    26. {
    27. if(n == 1)
    28. {
    29. return 1;
    30. }
    31. if (n == 2)
    32. {
    33. return 1;
    34. }
    35. else
    36. {
    37. return fibu(n - 2) + fibu(n - 1);
    38. }
    39. }
    40. private void button2_Click(object sender, EventArgs e)
    41. {
    42. int x = Convert.ToInt32(textBox1.Text);
    43. textBox2.Text = "Erg: " + fibu(x);
    44. }
    45. }
    46. }
    Lass das Asynchron laufen. Die Begriffe Task und Await sollten dich zum Ziel führen.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    ein Beispiel

    C#-Quellcode

    1. using System;
    2. using System.Threading.Tasks;
    3. using System.Windows.Forms;
    4. namespace WinFormsCS
    5. {
    6. public partial class FrmMain : Form
    7. {
    8. private int CurrentCounter = 0;
    9. private bool ContinueCounting = false;
    10. public FrmMain()
    11. {
    12. InitializeComponent();
    13. }
    14. private async void BtnStart_Click(object sender, EventArgs e)
    15. {
    16. ContinueCounting = true;
    17. await Task.Run(Count);
    18. MessageBox.Show($"Bin bis {CurrentCounter} gekommen.");
    19. }
    20. private void Count()
    21. {
    22. while (ContinueCounting)
    23. {
    24. ++CurrentCounter;
    25. }
    26. }
    27. private void BtnStop_Click(object sender, EventArgs e)
    28. {
    29. ContinueCounting = false;
    30. }
    31. }
    32. }

    setzt voraus, dass Du 2 Buttons auf Deinem Form hast: [Start] und [Stop].
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    @VB.neter0101 Schreib die Werte in die Console, da musst Du micht andauernd die MessageBox bestätigen:

    C#-Quellcode

    1. Console.WriteLine(fibu(i));
    Das Ergebnis siehst Du dann im Ausgabefenster unterhalb des Editorfensters im Studio.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    VB-Fragen über PN / Konversation werden ignoriert!

    mrMo schrieb:

    Lass das Asynchron laufen. Die Begriffe Task und Await sollten dich zum Ziel führen.

    VB.neter0101 schrieb:

    Könntest du mir vllt. anhand meines Codes ein Beispiel liefern?

    Wenn ich sowas sehe kriege, ich direkt nen Kamm ! :cursing:
    Anstatt mit den den genannten Begriffen, eigeninitiativ zu recherchieren, willst du nur code geliefert haben !
    Und die Tatsache, daß du dann auch gleich beliefert wirst, führt natürlich dazu, daß du es immer wieder so machst.

    @VaporiZed :thumbdown:
    Hmm hmm.
    Zu die Stichworte 'Task' und 'Async' gibts jede Menge im INet, und nicht nur gutes.
    Da als Anfänger was brauchbares/verständliches zu finden ist evtl. Glücksache (evtl. - ich habs nicht probiert).

    Die Gefahr, dasser den BeispielCode nur nimmt, und nix draus lernt, sehe ich in diesem Fall nicht ganz so hoch, weil die Anwendung selbst ist ja eine Lern-Anwendung.

    Aber vlt. lernt er tatsächlich nix draus, weil der BeispielCode enthält kein HintergrundWissen.
    Daher erlaube ich mir mal, auf ein von mir verzapftes Tutorial zu verlinken:
    Async/Await: Unblock Gui without any additional Line of Code
    (Der Artikel zeigt und bespricht zwar VB-Sources, aber c#-Sources sind auch dabei, für die das gesagte zu 100% ebenfalls gilt.)
    Den Artikel habich verzapft, weil mir tatsächlich im INet nichts bekannt ist, was vorführt, wie simpel Async/Await anwendbar ist.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Mir geht es darum, daß es eine gewisse Grundhaltung offenbart.
    Selber keinen Finger krumm machen, aber beliefert werden wollen.

    "Ich habe dies, das und jenes im Netz gefunden, verstehe es aber nicht, kann mir wer anhand eines Beispiels erklären wie das funktioniert"
    Würde zeigen, daß derjenige auch bereit ist selber etwas Energie aufzuwenden, anstatt sich bedienen zu lassen.

    Warum müssen hier eigentlich immer wieder, absolute Grundlagen sozialem Verhaltens diskutiert werden !
    @FormFollowsFunction wer im Glashaus sitzt... Warum pauschalisierst du hier und trägst nichts nützliches dazu bei (auf die Frage möchte ich keine Antwort haben...)? Es ist doch total vernachlässigbar, ob jemand etwas dazu lernt oder nicht, das kann dir doch egal sein. Diese Grundhaltung von der du sprichst kann ich verstehen, also nicht die Grundhaltung, aber zumindest deine "Befürchtung". Ich selbst bin ein stark frequentierter Beantworter von Mathefragen in diversen anderen Mathe-Foren und stelle dort gerne meine gesamten Rechenwege für Fragensteller zur Verfügung (An Beispielen kann man auch lernen...). Als ich meine Frage gestellt hatte, habe ich mir natürlich selbst schon Gedanken gemacht und die "Schlüsselbegriffe", die dann auch gefallen sind, habe ich mir auch soweit schon fast erdacht (ich hab damit bis jetzt nur noch nicht gearbeitet ;)). Diese Bestätigung, die dann von @mrMo gekommen ist, hat dann auch meinen Blick in die richtige Richtung bekräftigt. Parallel zur Frage hier habe ich auch selbst recherchiert und eine eigene Implementierung gebastelt und es verhält sich im Grunde genau wie @ErfinderDesRades das schon vorhergesagt hat, "Da als Anfänger was brauchbares/verständliches zu finden ist evtl. Glücksache (evtl. - ich habs nicht probiert)." kann ich bestätigen. Dann habe ich eben selbst etwas probiert, dass zumindest das macht was ich wollte, aber ohne das ganze Hintergrundwissen ist das schwierig zu beurteilen. Bei zukünftigen Antworten von dir (@FormFollowsFunction) könntest du dir ein Beispiel an dem Kommentar vom ErfinderDesRades nehmen, er hätte mich hier genau wie du kritisieren können, stattdessen hat hat der ErfinderDesRades jedoch noch qualitativ etwas dazu beigetragen (das ist Top!), was in jedem Fall deinen beiden Antworten vorzuziehen ist.

    Ich will hier keine Debatte auslösen (auch wenn ich befürchte, dass wir noch Antworten von @FormFollowsFunction zu erwarten haben :rolleyes: ... )

    Ich bedanke mich an dieser Stelle bei allen anderen Antwortern hier, die das Thema fachlich weitergebracht haben (PS: +1 für jeden von euch)!

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „VB.neter0101“ ()

    Jo, wenn wolle, könnteste deine Lösung hier auch einstellen, dann kann man auch gugge, ob du's richtig gelernt hast.
    Weil auch Vaporized lieferte ja keine Fertiglösung deines Problems, sondern eine annere, eiglich sogar interessantere Lösung (Nebenläufigkeit mit vom User ausgelöstem Abbruch).
    Wenn du dieses Lehrbeispiel korrekt auf deinen Code aus post#1 übertragen hast, dann haste zumindest einmal den async/await-Pattern korrekt angewendet, und ein Form ge-unblockt.

    Übrigens, @VaporiZed: Für Nebenläufigkeit mit vom User ausgelöstem Abbruch ist eigentlich die CancellationTokenSource-Struktur vorgesehen (siehe Tut).
    (Welche soweit ich sehen kann, aber keinen Vorteil gegenüber deiner Lösung bietet, ausser, dass sie eben der vorgesehene Standard ist. Vielleicht reagiert CancellationTokenSource exakter, während deine Lösung erst beim nächsten systemeigenen Threadwechsel auslöst)
    @VB.neter0101
    Sowas habe ich erwartet.
    Vollkommen an meiner Kritik vorbei, einschleimen bei den anderen (Hilfrech verteilen) und natürlich schwer beleidigt.
    Du hast auch nicht ernsthaft recherchiert, zumindest nicht länger als eine Minute (siehe Zeitstempel), denn dann wärst du schnell auf diesen Thread gestoßen.
    Der Versuch sich jetzt aus der Affäre zu lügen passt gut zu dem restlichen verhalten.
    @ErfinderDesRades Ich habe die Methode button2_Click(...) nun als asynchrone Methode definiert. Innerhalb dieser Methode habe ich dann mit await Task.Run(() => tester(x)); gearbeitet. Ich hatte mir diesbezüglich noch eine Methode ohne Rückgabewert (private void tester(int x){ ausg = fibu(x);}) definiert, in der dann die Methode fibu(x) aufgerufen wird. Das Ergebnis von fibu(x) wird dann in eine Variable gespeichert. Nach Task.Run... in der Methode des Buttons wird schließlich textBox2.Text = "Erg: " + ausg; aufgerufen und mir mein Ergebnis nach erfolgreicher Ausführung angezeigt.

    Code:
    Spoiler anzeigen

    C#-Quellcode

    1. using System;
    2. using System.Collections.Generic;
    3. using System.ComponentModel;
    4. using System.Data;
    5. using System.Drawing;
    6. using System.Linq;
    7. using System.Text;
    8. using System.Threading.Tasks;
    9. using System.Windows.Forms;
    10. namespace WindowsFormsApp2
    11. {
    12. public partial class Form1 : Form
    13. {
    14. private int ausg;
    15. public Form1()
    16. {
    17. InitializeComponent();
    18. }
    19. public static int fibu(int n)
    20. {
    21. if(n == 1)
    22. {
    23. return 1;
    24. }
    25. if (n == 2)
    26. {
    27. return 1;
    28. }
    29. else
    30. {
    31. return fibu(n - 2) + fibu(n - 1);
    32. }
    33. }
    34. private void tester(int x) {
    35. ausg = fibu(x);
    36. }
    37. private async void button2_Click(object sender, EventArgs e)
    38. {
    39. int x = Convert.ToInt32(textBox1.Text);
    40. await Task.Run(() => tester(x));
    41. textBox2.Text = "Erg: " + ausg;
    42. }
    43. }
    44. }

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VB.neter0101“ ()

    Neu

    @VB.neter0101 Mach doch gleich:

    C#-Quellcode

    1. private async void button1_Click(object sender, EventArgs e)
    2. {
    3. int x = Convert.ToInt32(textBox1.Text);
    4. await Task.Run(() => ausg = fibu(x));
    5. this.label1.Text = "Erg: " + ausg;
    6. }
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    VB-Fragen über PN / Konversation werden ignoriert!

    Neu

    Das ist schoma async funktionierend verwendet, schöpft aber immer noch nicht das Potential wirklich aus - die unschöne ausg-KlassenVariable ist entbehrlich.

    C#-Quellcode

    1. public static int fibu(int n) {
    2. if (n <= 2) return 1;
    3. return fibu(n - 2) + fibu(n - 1);
    4. }
    5. private async void button2_Click(object sender, EventArgs e) {
    6. int x = Convert.ToInt32(textBox1.Text);
    7. textBox2.Text = await Task.Run(() =>"Erg: " + fibu(x));
    8. }
    vielleicht doch das Tut lesen (zumindest den Anfang davon)?

    Neu

    @VB.neter0101 Wenn Du Deinen Fibonacci-Algorithmus nicht rekursiv, sondern in einer Schleife ablaufen lässt, ist er doppelt so schnell:

    C#-Quellcode

    1. static int nb1;
    2. static int nb2;
    3. static int fibo(int n)
    4. {
    5. if (n < 2)
    6. {
    7. return 1;
    8. }
    9. int value = 0;
    10. for (int i = 1; i < n; i++)
    11. {
    12. value = nb1 + nb2;
    13. nb1 = nb2;
    14. nb2 = value;
    15. }
    16. return value;
    17. }
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    VB-Fragen über PN / Konversation werden ignoriert!

    Neu

    @ErfinderDesRades, danke auch für deine Antwort! Ich bin gerade dabei dein Tut zu lesen (das wäre ein schöner Inhalt für ein Grundlagenbuch) :)

    @RodFromGermany die iterative Variante der Fibu-Folge ist mir bekannt und der Geschwindigkeitsvorteil auch :) Ich hab das nur rekursiv gemacht, weil ich wusste, dass dadurch (bei großen Eingaben) die Form schneller "einfriert", sodass ich dann mit dem async mal testen konnte/könnte, ob das eine Lösung ist.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VB.neter0101“ ()

    Neu

    @VB.neter0101 Auch diese Variante kannst Du async aufrufen. ;)
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    VB-Fragen über PN / Konversation werden ignoriert!