GUI über Hintergrundthread ansprechen

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

Es gibt 24 Antworten in diesem Thema. Der letzte Beitrag () ist von StGo.

    GUI über Hintergrundthread ansprechen

    Hallo zusammen,

    als ich gestern auf einen ähnlichen Post geantwortet habe ist mir danach etwas aufgefallen.

    Wenn ich einen Hintergrundthread starte und nebenläufig dazu den Hauptthread die gleiche Prozedur durchlaufen lasse bekomme ich ein mir nicht erklärbare Ausgabe.

    Das lässt mich vermuten, dass der GUI-Thread den Hintergrundthread blockiert.
    Kann mir jemand sagen was ich hier falsch mache?

    Danke

    Problem ist in den folgenden Posts gelößt worden.

    Code aus meiner Frage ist von mir gelöscht worden!!!

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

    Natürlich hält die GUI die Threads auf. Invoke wartet auch, bis der komplette Code aufgerufen wurde. Die Thread-Synchronisation+Anzeige braucht Zeit, solange läuft dein Thread nicht weiter. Man könnte BeginInvoke verwenden, dann wartet der Thread nicht mehr bis die Aktion erledigt ist, dann können die Nachrichten natürlich auch verzögert ankommen. In diesem Fall würden sich die Invokes nur Auflisten und dir deinen GUI-Thread einfrieren.
    -> Zeige nur etwas auf der GUI an, wenn es nötig ist, ein Nutzer benötigt normalerweiße nicht jede ms ein Update zum Thread.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    StGo schrieb:

    Das lässt mich vermuten, dass der GUI-Thread den Hintergrundthread blockiert.
    Ja, bei Control.Invoke blockiert der Gui den Backgroundthread.
    Deshalb rede ich zB ausnahmslos von Control.BeginInvoke.

    Und bei MSDN steht großer Murks. Also was da steht, funzt zwar, aber dass das deswegen "richtig" sei, kann man imo nicht sagen.

    Etwa dieses:

    VB.NET-Quellcode

    1. Private Sub KomplexeBerechnung(ByVal threadnr As Integer)
    2. For i As Integer = 0 To 10
    3. Thread.Sleep(500)
    4. Me.BeginInvoke(Sub() txtResult.AppendText("ThreadId: " & threadnr & _
    5. " KomplexeBerechnung wird berechnet" & Environment.NewLine))
    6. Next
    7. End Sub
    ist viel richtiger, denn
    1. man braucht keinen Extra-Delegaten - zur Not nehme man halt Action(Of T)
    2. man übergibt keine threadnr As Object, wenns ein Integer ist
    3. Man braucht nicht auf InvokeRequired zu testen, wenn man genau weiß, dass Invoking required ist
    4. Man braucht für sone PillePalle-Aktion wie den Text anzuhängen überhaupt gar keine eigene Methode
    Das liebe ich so in Foren. Die Diskussion ob dieser Code sinnvoll ist oder nicht.

    Zeige nur etwas auf der GUI an, wenn es nötig ist, ein Nutzer benötigt normalerweiße nicht jede ms ein Update zum Thread.

    Es soll zeigen das sich die Threads abwechseln da es ein Beispielcode ist für eine Arbeit in der UNI ist.


    man übergibt keine threadnr As Object, wenns ein Integer ist

    Ja ist mir klar. Ich brauch es aber für ein weiteres Beispiel, wo ich dann dies übergebe:

    VB.NET-Quellcode

    1. Dim parameter = New Object() {hintergrundThread, _
    2. "text"}


    Man braucht nicht auf InvokeRequired zu testen, wenn man genau weiß, dass Invoking required ist

    Na ja auch der GUI-Thread verwendet die Prozedur Komplexe Berechnung und der nutzt den else Zweig. Aber wie gesagt es ist ein Pattern aus dem MSDN und für jemand der nicht 10Jahre Erfahrung hat ist das der erste Anlaufpunkt wenn er ein Problem hat.

    VB.NET-Quellcode

    1. Man braucht für sone PillePalle-Aktion wie den Text anzuhängen überhaupt gar keine eigene Methode

    Wie gesagt es ist ein Beispiel zum Multithreading in der ich in einem kurzem Codebeispiel zeigen möchte wie das Prinzip funktioniert.

    Trotzdem Danke für eure Hilfe

    StGo schrieb:

    Könntest du mir einen kleinen Tip geben?

    StGo schrieb:

    Den Code konnte ich gerade nicht testen.
    Teste den Code, sieh Dir dabei die CPU-Auslastung im Task-Manager an.
    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!
    Das liebe ich so am Forum, dass den Fragen Code beigefügt wird, der gar nicht zur Diskussion steht.

    Doch er steht zur Diskussion. Er zeit mein Problem. Es ist eine Minibeispiel was dazu da ist ein Problem aufzuzeigen und keine praktische Anwendung ist.

    Ich habe eben im C# Forum einen Beitrag zum Thema gelesen aus dem ich etwas zitieren möchte:

    Unterschiede von Control.Invoke und Control.BeginInvokeIn vielen Fällen kann man wählen, ob man Control.BeginInvoke oder Control.Invoke benutzt. Der Hauptunterschied ist, dass Control.Invoke wartet, bis der GUI-Thread die Aktion ausgeführt hat, wogegen Control.BeginInvoke den Arbeitsauftrag nur in die Nachrichtenschlange des GUI-Threads stellt und sofort zurückkehrt. Control.Invoke arbeitet also (in vielen Fällen unnötig) synchron. Allerdings muss man bei Control.BeginInvoke jegliche erforderliche Synchronisation beim gleichzeitigen Zugriff auf dieselben Daten selbst realisieren. Ein weiter Unterschied ist, dass man bei Control.Invoke leichter an einen evtl. Rückgabewert der Aktion kommt als bei Control.BeginInvoke.Wenn man Control.BeginInvoke benutzt und später doch noch an den Rückgabewert der Aktion kommen möchte oder später doch noch auf die Beendigung der Aktion warten möchte, kann man Control.EndInvoke benutzen. Control.EndInvoke arbeitet synchron und kehrt erst zurück, nachdem die Aktion ausgeführt wurde. Der Aufruf von Control.EndInvoke ist optional, also nicht erforderlich.


    Link der Quelle: mycsharp.de/wbb2/thread.php?threadid=33113

    Ich denke die Erklärung ist nicht schlecht. Sie spiegelt ja auch in etw das wieder was EDR sagt.

    Wer das Minibeispiel hernimmt und BeginInvoke testet bekommt die gleiche Ausgabe wie mit dem MSDN Pattern. Die Ausgabe des Hintergrundthreads wird eingereiht und ausgegeben wenn der GUI Thread fertig ist. Das hilft mir natürlich nicht für mein Beispiel da ich zeigen möchte, dass die Threads sich abwechseln aber wenigstens habe ich jetzt den Grund dafür.
    Warum das Ergebnis des Pattern trotz Invoke die Ausgabe an das Ende des GUI Threads hängt ist mir zwar immer noch nicht klar aber ich werde es ohne das Pattern zeigen.

    Gruß

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

    StGo schrieb:

    Warum das Ergebnis...
    ist das das Ergebnis vom gezeigtne Code?
    Also wenn du wirklich das Thread.Sleep auskommentiert hast, dann läuft deine "komplexe Berechnung" vermutlich mit soner affenartigen Geschwindigkeit durch, dass das Thread-Management da nicht einen einzigen Threadwechsel dazwischengeklemmt kriegt.
    ​ist das das Ergebnis vom gezeigtne Code?


    Ja ist es :) Aber genau das was du geschrieben hast habe ich mir auch gedacht. Darum das sleep. Ich habe auch den GUI Thread um 1000ms verzögert um ein anderes Ergebnis zu "erzwingen". Aber auch das hat nicht geholfen.

    VB.NET-Quellcode

    1. ​Private Sub ThreadStartBeispiel()
    2. Dim hintergrundThread As New Thread(AddressOf KomplexeBerechnung)
    3. hintergrundThread.IsBackground = True
    4. hintergrundThread.Start(1)
    5. Thread.Sleep(1000)
    6. KomplexeBerechnung(2)
    7. End Sub
    ja, also von diesem Code nun würde ich auch annehmen, dass zuerst threadnr#1 ausgegeben wird, dann #2.

    Ist dem nicht so, dann fasse ich es nicht.
    Soll ich dir ein Beispiel basteln? Ich muss aber sagen, ich bevorzuge ganz stark das modernere Task-Threading-Modell. Also wenn du mit FW4.5 arbeitest, dann ist dein bisheriges eh heillos antiquiert.

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

    StGo schrieb:

    Ich habe auch den GUI Thread um 1000ms verzögert
    Hast Du Dir meinen Code ühaupt mal angesehen?
    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!
    Soll ich dir ein Beispiel basteln? Ich muss aber sagen, ich bevorzuge ganz stark das modernere Task-Threading-Modell. Also wenn du mit FW4.5 arbeitest, dann ist dein bisheriges eh heillos antiquiert.


    Ich vergleiche die Möglichkeiten des Threadings. Es ist nur ein Beispiel in einer Textpassage. Da ich aber nicht Pseudocode schreiben wollte sonder Code den man auch ausprobieren kann stelle ich fest, das es nicht immer so ist wie man es sich in der Theorie vorstellt.

    ​Hast Du Dir meinen Code ühaupt mal angesehen?


    Ja inzwischen schon. Aber ich sehe den Zusammenhang nicht. Möchtest du mir sagen ich soll den Threadpool anstelle einzelner Threads nehmen? Wenn ja kann ich nur nochmal wiederholen das es sich um eine Arbeit für die Uni handelt in der ich die einzelnen Konzepte vergleiche. Falls ich was übersehe in deinem Code wäre ein Hinweis gut. Denn ich suche keine Alternativlösung zum Thread sondern frage mich warum das MSDN Pattern ein anderes Ergebnis erzeugt.

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

    StGo schrieb:

    Aber ich sehe den Zusammenhang nicht.
    Lernresistent?
    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!
    Lernresistent?


    Denke ich nicht. Da meine Frage mein Interesse am Thema zeigt und für meine Arbeit die Antwort keine Rolle spielt. Ich möchte eben wissen wie es richtig/besser geht.

    Dann häng du doch deine TestSolution an


    Ich kann das Projekt anhängen. Da in der Solution zu viele Projekte drin sind.
    Dateien
    Hmm - ich finde überhaupt kein problematisches Verhalten - ich hab sie alle durchprobiert - mit welchem ist Problem?
    Hier meine Comments:

    VB.NET-Quellcode

    1. ''' <summary>
    2. ''' Einstiegspunkt. Bitte die gewünschte Methode einkommentieren.
    3. ''' </summary>
    4. Private Sub cmdStart_Click(sender As Object, e As EventArgs) Handles cmdStart.Click
    5. 'ThreadStartBeispiel() ' Ok
    6. 'ThreadJoinBeispiel() ' Ok
    7. 'ThreadAbortBeispiel() ' Ok
    8. 'ThreadInterruptBeispiel() ' versteh ich nicht
    9. ThreadPriorityBeispiel() ' keine merkliche auswirkung - vmtl., weil bei nem schlafenden Thread die Priority egal ist.
    10. End Sub


    Das ThreadJoin-Beispiel bringt übrigens nichts, denn man joint nicht mit dem Gui-Thread - das blockiert den ja.
    Man verjoint mehrere Nebenthreads, und wenn alle fertig sind - dann beauftragt man den Gui-Thread mit der Anzeige der Ergebnisse.

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

    ​Das ThreadJoin-Beispiel bringt übrigens nichts, denn man joint nicht mit dem Gui-Thread - das blockiert den ja.Man verjoint mehrere Nebenthreads, und wenn alle fertig sind - dann beauftragt man den Gui-Thread mit der Anzeige der Ergebnisse.


    Verstehe ich das falsch das ich den Thread den ich aus einem anderen starte auch nur mit diesem wieder Joinen kann? Ich kann doch nicht zwei unabhängige Threads miteinander Joinen?

    Im Prinzip wartet der Hauptthread doch nur auf den Nebenthread.

    ​Hmm - ich finde überhaupt kein problematisches Verhalten


    Ja die kleinen Beispiele funktionier auch. Das stand ja auch gar nicht zur Diskussion. Nimm ThreadStartBeispiel() Methode und lasse einmal die Ausgabe in der KomplexenBerechnung über das Pattern (Me.SetText(...)) und einmal über Me.Invoke(Sub() ...) laufen udn schau auf die Ausgabe. Dann sollten die ähnlich meinen beiden Screenshots sein.

    Die Frage ist warum gibt das eine unterschiedliche Ausgabe?

    Danke