Warum wird mein WaitScreen länger angezeigt als er sollte, wenn der Hauptthread ausgelastet ist?

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 13 Antworten in diesem Thema. Der letzte Beitrag () ist von John422.

    Warum wird mein WaitScreen länger angezeigt als er sollte, wenn der Hauptthread ausgelastet ist?

    Hallo allerseits,

    ich habe einen WaitScreen, den ich anzeige, wenn eine Operation eine Weile dauern kann und ich sie nicht auslagern kann/möchte.

    Leider wird mein Screen zu lange angezeigt, wenn mein Hauptthread "ordnetlich beschäftigt" war. Hat er wenig zu tun gehabt, passt alles.

    Ich verstehe es einfach nicht und wäre dankbar, wenn Ihr mal einen Blick daauf werfen könntet.

    Viele Grüße

    John
    Dateien
    • WaitScreen.zip

      (48,04 kB, 49 mal heruntergeladen, zuletzt: )
    @John422 Dein Waitscreen wird ordentlich beendet.

    VB.NET-Quellcode

    1. Me.Text = i.ToString
    hält das Programm auf.
    Ersetze es durch

    VB.NET-Quellcode

    1. Console.WriteLine(i.ToString)
    und Du wirst es sehen.
    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).
    Programmierfragen über PN / Konversation werden ignoriert!
    Sorry, das verstehe ich nicht.

    VB.NET-Quellcode

    1. For i = 1 To 50000
    2. Me.Text = i.ToString
    3. Next
    4. Wait.Close()


    Diese Dummy-Schleife deint doch nur zur Verdeutlichung des Problems. In meinem richtigen Prokekt passiert das etwas sinnvolles, z.B. werden viel Objekte erzeugt und das Problem ist ebenfalls vorhanden.

    Ausserdem tritt das Problem ja erst NACH dem Wait.Close am Ende der Schleife auf. Das sieht man daran, daß der WaitScreen in der Closing Methode

    VB.NET-Quellcode

    1. Me.lblHeadline.Text = "Was mache ich noch hier?"
    2. Me.Update()


    macht. Danach müßte das Form sofort weg sein. Ist es aber nicht. Es bleibt eine Weile weiter zu sehen, während die Schleife längst durch ist.
    Dass das überhaupt funktioniert wundert mich:

    VB.NET-Quellcode

    1. Public Class clsWait
    2. Dim waitForm As frmWait
    3. Public Sub Show(Parent As Form, sHeadline As String)
    4. waitForm = New frmWait
    5. waitForm.Init(Parent, sHeadline)
    6. Dim thread As New System.Threading.Thread(AddressOf LoadingProcessEx)
    7. thread.Start()
    8. End Sub
    9. Public Sub Close()
    10. waitForm.BeginInvoke(New System.Threading.ThreadStart(AddressOf waitForm.Close))
    11. waitForm = Nothing
    12. End Sub
    13. Private Sub LoadingProcessEx()
    14. waitForm.ShowDialog()
    15. End Sub
    16. End Class
    waitForm wird im MainThread erstellt (Zeile #5), im NebenThread geöffnet (#17), im waitForm-Thread wieder geschlossen (#12).
    Wobei ich die Orientierung verloren hab, ob der waitForm-Thread nun der MainThread ist oder nebenläufig.

    Du kannst dir mit

    VB.NET-Quellcode

    1. Debug.Print(Threading.Thread.CurrentThread.ManagedThreadId)
    eine Thread-Id ausgeben lassen, um zu gucken, in welchem Thread was passiert.



    Ansonten läuft das bei mir auch nicht zulange. Wenn der Hoch-Zähler 50000 erreicht hat, geht bei mir das WaitForm sofort zu.

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

    @John422 Ich habe Dein Problem vollständig verstanden.
    Ich habe Dein Problem reproduziert.
    Ich habe lediglich Deine Dummy-Ausgabe modifiziert.
    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).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany die Dummy-Ausgabe zu modifizieren löst aber mein Problem nicht, weil in meiner realen Anwendung die Programmschritte, die dort stehen ebenfalls das Problem auslösen und ich sie nicht durch etwas anderes ersetzten kann.

    @ErfinderDesRades bei Dir bleibt das Fenster nicht ca. 5 Sekunden mit der Anzeige "Was mache ich noch..." stehen, sondern ist bei 50.000 sofort weg? Wird ja immer mysteriöser.
    Bei mir bleibt es stehen, aber ich kann dir auch gerade nicht sagen warum. Du blockierst ja den UI-Thread der Form mit viel Arbeit und machst trotzdem UI-Updates, wie z.B. den Text zu setzten. Ich glaube da staut sich einfach einiges auf, was dann am Ende erstmal abgearbeitet werden muss. Das merkst du auch daran, dass nicht nur das Lade-Fenster stehen bleibt, sondern die komplette UI einfriert. Du kannst das Lade-Fenster auch wegnehmen und am Ende der Schleife bleibt die UI trotzdem diese Zeit lang stehen. Ich glaube du musst die "schwere" Arbeit in einen Task auslagern und darfst du UI nicht so lange blockieren.
    Bei mir bleibt das Form auch etwa 7 Sekunden lang noch da. Da scheint der GUI Thread nicht mehr mitzukommen.
    Habs auch mal versucht, brauchte sowas nie zuvor. Zumindest bleibt das Form dann nur halb so lange noch da, keine wirkliche Lösung, aber besser.

    C#-Quellcode

    1. public partial class FormMain : Form
    2. {
    3. FormWait formWait;
    4. public FormMain()
    5. {
    6. InitializeComponent();
    7. }
    8. private void Loading()
    9. {
    10. for (int i = 0; i < 50000; i++)
    11. {
    12. Invoke(new Action(() => Text = i.ToString())); //damit der GUI Thread noch mitkommt, auf fertigstellung warten daher Invoke statt BeginInvoke
    13. }
    14. BeginInvoke(new Action(() => formWait.Close()));
    15. }
    16. private void FormMain_Shown(object sender, EventArgs e)
    17. {
    18. formWait = new FormWait();
    19. Thread t = new Thread(Loading);
    20. t.Start();
    21. formWait.ShowDialog();
    22. }
    23. }

    John422 schrieb:

    @ErfinderDesRades bei Dir bleibt das Fenster nicht ca. 5 Sekunden mit der Anzeige "Was mache ich noch..." stehen, sondern ist bei 50.000 sofort weg?
    Jo - so isses.
    Liegt vlt. an meim Windows-Style - ich verwende klassisches Design - nicht diesen komischen Blurry-Style, oder was danach noch auf den Markt geschissen wurde.
    Da ist schon früher aufgekommen, dass bei dem Neu-Kram zB Progressbars nur verzögert reagieren - also einzig im Classic Design zeigt eine PB während des Fortschritts diesen auch korrekt an.
    Also vlt. mal in Classic probieren, obs daran liegt.

    Was du auch machen könntest, ist die von mir genannten Ungereimtheiten deiner Threading-Umsetzung bereinigen.
    Als drittes kannst du ja auch mal mein IsBusy-Dialog - Tutorial ausprobieren, im Tutorial-Bereich.

    ErfinderDesRades schrieb:

    Also vlt. mal in Classic probieren, obs daran liegt.
    Ich hab klassisch, da bremst es auch.
    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).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich habe mit mehreren Ansätzen versucht, die zu erledigende Aufgabe nun versuchsweise in einen BackgroundWorker auszulagern.

    Dazu übergebe ich dem Worker eine Instanz einer Hilfklasse mit den Daten, die er Verarbeiten soll. Mit wenigen Daten funktioniert das, mit vielen nicht mehr mit der Meldung "System.ArgumentException: "Das Zielarray ist nicht lang genug."

    Irgendwie ist das total frustierend.
    Es läuft ja alles eigentlich wunderbar im Hauptthread. Für den simplen Wunsch einer WaitWindows fange ich nun an, meine Daten zur Verarbeitung in einen anderen Thread auslagern zu müssen. Irgendwie ist das doch von hinten durch die Beine...
    so ist das eben.
    Wenn deine langdauernde Methode im Hauptthread läuft, blockiert sie diesen, und du kannst nichts anzeigen - auch keinen Wait-Dialog.

    Bzw kannste doch - indem du den Wait-Dialog nebenläufig machst.
    Was du aber so wohl kaum hinbekommen wirst: Dass vom Wait-Dialog aus die Operation gecancelt wird.

    Ausserdem ists doch gehupft wie gesprungen: Entweder der Wait-Dialog muss in den NebenThread oder die Operation.

    Üblicherweise macht man die Operation nebenläufig und reserviert den HauptThread fürs GUI - und da macht man auch keine Misch-Lösungen mit Forms in verschiedenen Threads.
    Ausnahme: Der Splash-Screen beim Startup ist normal nebenläufig. Aber da will ja auch niemand canceln.

    Komischerweise erwähnst du das IsBusy-Dialog-Tutorial, was ich empfahl, mit keinem Wort.
    Scheinbar bist du drauf erpicht, dir selbst was auszudenken, ohne Rückgriff auf mehr oder weniger erprobte Vorlagen.
    OK, ich habs gefunden.

    Es ist, wie @RodFromGermany vermutete einzig und alleine das

    VB.NET-Quellcode

    1. Me.Text = i.ToString


    Das hatte ich versehentlich zu Testzwecken auf in meiner eigentlichen Anwendung drin. Nimmt man es raus, ist das Problem weg.

    @ErfinderDesRades den IsBusy-Dialog hatte ich mir für heute vorgenommen. Eine schöne Lösung, die ich bestimmt an anderer Stelle mal einsetzen werden.

    Vielen Dank an Euch alle!