Verständnis Threads, Async/Await, Backgroundworker

  • VB.NET
  • .NET 4.5

Es gibt 22 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Verständnis Threads, Async/Await, Backgroundworker

    Hi,

    ich schaue mir gerade die Backgroundworker Klasse an. Es scheint in Richtung Threading zu gehen, da ich schon ein paar andere Konzepte dazu kennengelernt habe, frage ich mich was es da für Unterschiede gibt. Ich versuche mal Schritt für Schritt meine Fragen zu stellen.
    Im Beispielcode steht direkt in der ersten Zeile etwas das ich nicht verstehe. Was bedeutet hier InitializeComponent()?
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.ComponentModel
    2. Public Class Form1
    3. Public Sub New()
    4. InitializeComponent()
    5. backgroundWorker1.WorkerReportsProgress = True
    6. backgroundWorker1.WorkerSupportsCancellation = True
    7. End Sub
    8. Private Sub startAsyncButton_Click(ByVal sender As System.Object, _
    9. ByVal e As System.EventArgs) Handles startAsyncButton.Click
    10. If backgroundWorker1.IsBusy <> True Then
    11. ' Start the asynchronous operation.
    12. backgroundWorker1.RunWorkerAsync()
    13. End If
    14. End Sub
    15. Private Sub cancelAsyncButton_Click(ByVal sender As System.Object, _
    16. ByVal e As System.EventArgs) Handles cancelAsyncButton.Click
    17. If backgroundWorker1.WorkerSupportsCancellation = True Then
    18. ' Cancel the asynchronous operation.
    19. backgroundWorker1.CancelAsync()
    20. End If
    21. End Sub
    22. ' This event handler is where the time-consuming work is done.
    23. Private Sub backgroundWorker1_DoWork(ByVal sender As System.Object, _
    24. ByVal e As DoWorkEventArgs) Handles backgroundWorker1.DoWork
    25. Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
    26. Dim i As Integer
    27. For i = 1 To 10
    28. If (worker.CancellationPending = True) Then
    29. e.Cancel = True
    30. Exit For
    31. Else
    32. ' Perform a time consuming operation and report progress.
    33. System.Threading.Thread.Sleep(500)
    34. worker.ReportProgress(i * 10)
    35. End If
    36. Next
    37. End Sub
    38. ' This event handler updates the progress.
    39. Private Sub backgroundWorker1_ProgressChanged(ByVal sender As System.Object, _
    40. ByVal e As ProgressChangedEventArgs) Handles backgroundWorker1.ProgressChanged
    41. resultLabel.Text = (e.ProgressPercentage.ToString() + "%")
    42. End Sub
    43. ' This event handler deals with the results of the background operation.
    44. Private Sub backgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, _
    45. ByVal e As RunWorkerCompletedEventArgs) Handles backgroundWorker1.RunWorkerCompleted
    46. If e.Cancelled = True Then
    47. resultLabel.Text = "Canceled!"
    48. ElseIf e.Error IsNot Nothing Then
    49. resultLabel.Text = "Error: " & e.Error.Message
    50. Else
    51. resultLabel.Text = "Done!"
    52. End If
    53. End Sub
    54. End Class


    Der Backgroundworker erzeugt von sich aus andere Threads, das heißt diese Threads kann ich theoretisch selbst genauso gestalten und es dreht sich nur um die Komfortabilität oder gibt es da Besonderheiten, die anders nicht gehen?
    Ich finde das auch ähnlich zu Async, Await und Task, können sich diese Konzepte ebenso entsprechen?
    Also die Sub InitializeComponent() findest du in der Form.Designer.vb datei, siehe Anhang, klappe im Projektmappnexplorer bei einem Form den Filter auf, dort findest du sie.

    Also ein Backgroundworker ist wie ein Thread, nur mit einigen Extras. Wie du da siehst auch mit Progress und Abbruchmöglichleiten. Kurz würde ich sagen die vereinfachte Art Threads anzuwenden. Mit einem einfachen Thread muss man alles selbst machen, auch das Abrechen wird bald nicht mehr einfach mit Thread.Abort gehen(wird schon als veraltet im Studio angememahnt), da muss man dann selbst noch einen CancellationTokenSource für einen anständigen Abbruch nutzen.

    Das andere wird jemand besser erklären können, Async Await habe ich nie gebraucht, habe daher auch so gut wie nie was dazu gelesen, ausser einfache Anwendung, bin Altmodisch und nutze nur Threads.
    Bilder
    • Unbenannt.png

      10,31 kB, 813×67, 16 mal angesehen
    Die Natur ist bekanntermaßen knallhart, sie sortiert aus was sich nicht bewährt hat.(Harald Lesch, 2021)

    Demnach müssten wir bald dran sein...
    Ist der Backgroundworker denn "modern"?

    Das ist ja blöd mit der InitializeComponent, die haben den Code ja quasi ohne Projekt stehen. Warum wird da überhaupt ein New() Sub gemacht? Die läuft doch garnicht

    Also gehe ich richtig davon aus das für einen anderen Thread nochmal die nötigen Bedienfelder der Anwendungen generiert werden müssen? Das macht InitializeComponent dann, ist also auch nur Komfortabilität?
    @Haudruferzappeltnoch Die Prozedur InitializeComponent() sorgt dafür, dass Deine Form so aussieht, wie Du sie im Designer angelegt hast.
    Halbiere Deine GUI, rechts die Form, links die Prozedur InitializeComponent() und sieh Dir an, welche Änderungen vorgenommen werden, wenn Du Properties eines Controls änderst oder wenn Du neue Contgrols hinzufügst.
    Das New() wird vom Framework aus aufgerufen. Setze einen Haltepunkt rein und verfolge, was da passiert.
    Normalerweise wird die ansonsten leere Prozedur New() nicht in der Form-Datei eingefügt, die brauchst Du nir dann, wenn weitere Initialisierungen erforderlich sind.
    ====
    Der Backgroundworker ist ein Auslaufmodell.
    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!
    @Haudruferzappeltnoch Der Backgroundworker ist ein "normaler" Thread mit einigen Schnick-Schnacks, der den Code nebenläufig ausführt.
    Async-Await-Konstrukte führen den Code ebenfalls nebenläufig aus, nur dass die Organisation und das Handling inzwischen wesentlich elaganter sind und der Code leichter lesbar ist.
    Die Frage zunächst ist:
    Was genau willst Du nebenläufig tun?
    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!

    Haudruferzappeltnoch schrieb:

    Wird das dann alles mit Async Await gemacht ...?
    Ja.
    Backgroundworker ist Müll, und hätte eiglich schon 2005 aus dem Verkehr gezogen gehört.

    Versuche Threading nie anders als über Tasks, Async/Await zu lösen.
    Es gibt vielleicht Sonderfälle, wo man vlt. doch noch olle Kamellen rauskramen muss, wie ThreadPool, Thread und Kram - aber ich wüsste konkret nicht einen einzigen zu nennen.
    Aber auf keinen Fall: BackgroundWorker.
    @ErfinderDesRades Parallel.For und Parallel.ForEach() sind da noch Threading-Sachen, allerdings eher für Sachen, die parallelisierbar sind.
    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!
    Ok vielen Dank, damit haben sich dann alle weiteren Fragen bis auf eine erstmal erledigt.

    In einem Artikel zu Threads habe ich gelesen sie sind Bestandteil von Prozessen, die Prozesse heißen im englischen Tasks. Ist was du mit Tasks meinst also ein solcher Prozess? Oder ist das etwas anderes?

    RodFromGermany schrieb:


    Was genau willst Du nebenläufig tun?


    Es ging mehr um das Konzept nicht um eine spezielle Anwendung. Ich denke ich probiere erstmal das Tut von EDR zu Async/Await nachzuvollziehen, das mit den Tasks ist mir noch nicht ganz klar

    Haudruferzappeltnoch schrieb:

    mehr um das Konzept nicht um eine spezielle Anwendung.
    Klar kannst Du zählen und jedes Mal ne Sekunde warten, aber für eine Anwendung von Nebenläufigkeit such Dir vielleicht ein gutes Problem, z.B. das Sieb des Eratosthenes.
    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!

    Haudruferzappeltnoch schrieb:

    um das Ergebnis schneller zu erreichen oder?
    Nö, um das Konzept zu verstehen.
    Im Thread wird "hart gerechnet" und die GUI blockiert nicht.
    Wenn Du in diesem Zustand Dein Programm beendest und Du hast keine Vorkehrungen getroffen, läuft der Thread noch weiter. ;)
    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!
    Öhm, hast Du für das Weiterlaufen ein Beispiel, @RodFromGermany? Ich wage ja zu behaupten, dass alles beendet wird, wenn das Programm endet.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Threads laufen weiter, musst du dann im Studio den StopButton drücken oder im taskmanager abschiessen. Daher vorsicht bei endless-loops. Im FormClosing-Event stell ich einen Flag um, solange flag == true, weiterlaufen. Wenn man was nimm was den Thread blockiert wie beim tcpclient, muss man auch noch eine CancellationTokenSource nutzen zum canceln.

    VB.NET-Quellcode

    1. Private t As Threading.Thread
    2. Sub NonStop()
    3. While True
    4. Threading.Thread.Sleep(1)
    5. End While
    6. End Sub
    7. Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
    8. t = New Threading.Thread(AddressOf NonStop)
    9. t.Start()
    10. End Sub

    Die Natur ist bekanntermaßen knallhart, sie sortiert aus was sich nicht bewährt hat.(Harald Lesch, 2021)

    Demnach müssten wir bald dran sein...

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

    Stop. Da läuft das Programm ja weiter. Der GUI-Thread nicht, aber das Programm. Ich fragte aber entsprechend Aussage in Post#12, wenn das Programm beendet ist.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Haudruferzappeltnoch schrieb:

    Ich denke ich probiere erstmal das Tut von EDR zu Async/Await nachzuvollziehen, das mit den Tasks ist mir noch nicht ganz klar
    Mein hiesiges Tut erscheint mir heute üaberkandidelt - das deckt alles mögliche ab.
    Viel besser fokussiert ist das hier: codeproject.com/Articles/10296…ithout-any-additional-Lin

    Jo, der Begriff "Task" hat mehrere Bedeutungen.
    Im Zusammenhang mit Threading bedeutet es einfach "Thread". Raiders heisst jetzt Twix, und Thread heisst jetzt Task.
    Es gibt auch Klassen, die so heissen, und einen Namespace - es ist einfach alles nochmal neu erfunden worden.
    Auch Paralell.For fährt im Namespace Tasks herum - gehört also zu den Guten.
    Hallo hier nochmal,

    ich bin nochmal beim BGW bzw. einem nebenläufigen Thread.
    Wenn man mehrere BGWs oder entsprechend mehrere Threads nutzt, ist das ja eigentlich dazu da, dass sie parallel zueinander laufen können.

    Dementsprechend den zweiten Thread nach Beendigung des ersten Threads zu callen, macht die Parallelität ja wieder zunichte.

    Wenn ich auch eine geblockte GUI in Kauf nehmen kann bei einem längeren Prozess, dann würde ja nichtmal mehr ein einzelner Nebenthread Sinn machen, oder?

    Also die Frage ist eigentlich gibt es noch andere Beweggründe einen Nebenstrang zu haben?
    • Jo, das übliche ist schon, dass man das Gui nicht geblockt haben will.
    • Dann kann man manchmal bestimmte rechenintensive Operationen auf mehrere Kernel verteilen, und dadurch Wartezeiten halbieren oder auch vierteln. Stichwort Paralell.For.
      Kommt allerdings so gut wie nie vor - also ich habs noch nie gebraucht.
    • Dann treten bei Datenübermittlungen Situationen auf, wo die Quelle sehr unregelmässig generiert.
      also mal 3s lang garnix, und dann aber in 5ms 200 Datensätze - sodass die DatenVerarbeitung da in dem Moment nicht hinterherkommt.
      In solchen Fällen schafft man einen nebenläufigen Puffer, wo die Quelle reinschmeissen kann, und die Verarbeitung holt die Daten dann in ihrem Tempo wieder raus.
      Stichwort Producer-Consumer - Problem.
      Das geht aber auch nicht mit Async, sondern dazu nimmt man spezielle, threadsichere Collections.
    • Und man kann viele Anfragen gleichzeitig verschicken, zB an verschiedene Websites - jo mit solche Spielchen beschäftigt sich mein vbp-Async-Tut (was ich ja für Einsteiger bischen überkandidelt finde).
    Aber wie gesagt: Unblocking GUI ist mit Abstand der Hauptgrund für Nebenläufigkeit.



    @VaporiZed:

    VaporiZed schrieb:

    Öhm, hast Du für das Weiterlaufen ein Beispiel, @RodFromGermany? Ich wage ja zu behaupten, dass alles beendet wird, wenn das Programm endet.
    Naja - das Programm endet dann eben nicht.
    Also man schliesst das MainForm, und denkt ist fertig.
    Isses aber nicht, sondern nach 3 Sekunden mag der NebenThread sein Ergebnis abliefern wollen - Jo, Exception, weil das Gui ist ja garnet mehr da.
    Kann man glaub sogar mit den Samples von meim englischen Tut reproduzieren (wenn ich recht erinnere).

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