Vorgang asynchron ausführen

  • VB.NET

Es gibt 29 Antworten in diesem Thema. Der letzte Beitrag () ist von Breadsoft.

    Vorgang asynchron ausführen

    Guten Tag

    Ich würde gerne mal wissen, wie man einen Vorgang asynchron ausführt, also dass er den aufrufenden Thread nicht blockiert. Ich habe nämlich einige Methoden in meiner DLL, welche manchmal etwas länger dauern. Was muss ich dann in meinen Subs/Functions einfügen damit das alles asynchron läuft.

    Vielen Dank im Voraus

    Breadsoft
    Wieso schreibst du bei Task 4.5 hin? Das gibts schon länger. Außerdem bin ich total dagegen jedem Anfänger nur noch Async Schlüsselwort zu zeigen. Es gibt jede Menge anderer Features die .Net anbietet. Und das wichtigste wäre dabei wohl immer noch nen simpler Thread: msdn.microsoft.com/de-de/libra…tem.threading.thread.aspx
    Dann gibts noch für Schleifen usw. um diese optimiert arbeiten zu lassen: msdn.microsoft.com/en-us/libra…ading.tasks.parallel.aspx
    Außerdem msdn.microsoft.com/de-de/libra…threading.threadpool.aspx

    Außerdem findest im Netz zahlreiche Anleitungen... einfach mal googlen. Bin in google gleichma auf die hier gestoßen(nur als Beispiel - ist aber c# aber Prinzip ist das Selbe): 1 2


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    Wie die Links von AliceDevil zeigen , gibt es eine Unmenge an Möglichkeiten das zu bewerkstelligen.

    Für diejenigen, die z.B. aus Kompatibilitätsgründen noch nicht auf die schöne neue Task-Welt umsteigen möchten , gibt es noch die Wahl zwischen
    - Backgroundworker (für mich mittlerweile obsolet)
    - Threadpool Threads (kurze Laufdauer) , z.B. über Control.BeginInvoke
    - den guten alten Thread

    Beispiel für Letzteren:

    VB.NET-Quellcode

    1. ...
    2. ' Thread anlegen: wichtig .isBackground=true, da dann der Thread endet wenn Form geschlossen wird
    3. Dim t As New Thread(Sub() ChangeWindowText(10)) With {.IsBackground = True, .Name = "ChangeWindow Thread"}
    4. t.Start()
    5. ....
    6. Sub ChangeWindowText(ByVal count As Integer)
    7. For i = 0 To count
    8. ' Controls laufen auf dem GUI Thread, daher muss zum Ändern der Thread mit .Invoke geswitched werden
    9. ' eigentlich müsste nach dem Invoke ein Delegat mit der Adresse einer Subroutine angegeben werde
    10. ' aber ab Visual Studio 10 geht es auch mit anonymer Methode (SUB() ...)
    11. frm.Invoke(Sub() frm.Text = String.Format("{0:HHH.mm.ss}", DateTime.Now))
    12. Thread.Sleep(1000)
    13. Next i
    14. ' fertig
    15. frm.Invoke(Sub() frm.Text = "Fertig")
    16. End Sub

    Oft ist es nötig zu wissen wann der Thread fertig ist: in dem Fall ein Event auslösen oder dem Thread einen Delegaten ( die Adresse einer Funktion) mitgeben die er aufrufen soll wenn er sich beendet. Kommt letztendlich aufs Gleiche raus.
    Es gibt 3 Muster, nach denen man arbieten kann, was asynchrone Operationen betrifft.

    Event-based Asynchronous Pattern (EAP) <-- Bisher am häufigsten verwendet
    Asynchronous Programming Model (APM)
    Task-based Asynchronous Pattern (TAP; nicht Async CTP, so hieß die Community Preview)

    Mehr Infos hier: msdn.microsoft.com/en-us/library/jj152938.aspx

    Das Neueste zu verwenden muss aber nicht immer gut/das Beste sein. Es gibt Gründe, ältere Methoden zu verwenden. Ich zitiere hier mal aus der_kurts Signatur:
    Ich darf zum Brotschneiden kein Messer benutzen, denn "Messer ist veraltet", heute nimmt man eine Brotschneidemaschine, die kann viel mehr...
    Führt Euch das mal vor Augen.

    Hier mal ein paar Codes, die das gleiche asynchron erledigen:

    Event-based:

    VB.NET-Quellcode

    1. Private Sub EinSub()
    2. Dim a As New WebClient()
    3. AddHandler a.DownloadStringCompleted, AddressOf DownloadCompleted ' Sagen, welchen Sub er nach dem DL ausführen soll
    4. a.DownloadStringAsync() 'Dl starten
    5. End Sub
    6. Private Sub DownloadCompleted(sender As Object, e As DownloadStringCompletedEventArgs)
    7. ' Wird aufgerufen, wenn DL fertig ist
    8. MessageBox.Show(e.Result)
    9. End Sub


    Task-based:

    VB.NET-Quellcode

    1. Private Async Sub EinSub()
    2. Dim a As New WebClient()
    3. Dim result As String = Await a.DownloadStringTaskAsync() ' awaitable Task holen und diesen awaiten (starten)
    4. ' result wird befüllt und es wird danach weiter gemacht
    5. MessageBox.Show(result)
    6. End Sub
    (APM ausgelassen, da der WebClient dieses nicht unterstützt)

    Einen Verleich von allen drei Patterns findet Ihr beim "Mehr Infos"-Link weiter oben im Post.

    Natürlich sieht das untere leichter und schöner aus. "Schöner" ist es auch. Beide Patterns haben aber ihre Schwächen. Das obere Pattern braucht einen Callback-Sub, den man aber auch in eine Lambda-Expression auslagern kann:

    VB.NET-Quellcode

    1. Private Sub EinSub()
    2. Dim a As New WebClient()
    3. a.DownloadStringCompleted += Sub(s, e)
    4. ' Wird aufgerufen, wenn DL fertig ist
    5. MessageBox.Show(e.Result)
    6. End Sub
    7. a.DownloadStringAsync() 'Dl starten
    8. End Sub


    Wenn man einen einfachen Thread verwendet, lässt sich ein Event-basiertes system leichter implementieren. Verwenden (besonders für externe Leute) lässt sich aber das TAP schöner. Intern ist TAP übrigens Callback-Basiert (ist "nur" eine Compilererweiterung; einfach mal mit dem Reflector schauen, was der daraus macht).

    Trotzdem fragt sich jemand, der noch nie normales Threading verwendet hat: "Wie geht das? Warum verlässt er bei Await den Sub und macht dann hinterher weiter? Was sind das für komische Tasks? Wie geht das überhaupt, dass der das im Hintergrund macht? Was ist hier los?"

    Noch schlimmer wird es, wenn dieser Jemand nicht einmal die Grundlagen vollständig beherrscht: "Was ist dieses (Of Bla)? Callback? Delegate? IProgress(Of T)? Interface? CancellationToken? SynchronizationContext? Wie soll man das benutzen??"

    Der TAP-Kram erfordert weitaus mehr Hintergrundwissen als andere Alternativen (Event-based, Thread-Klasse, Backgroundworker, usw.).

    Warum also kein TAP?
    Dagegen:
    - Schwerer zu verstehen (einfach über all Async und Await hinklatschen macht es um Welten schlimmer, als wenn man einfach Eventbasiert arbeiten würde)
    - Erst ab .NET 4.0 mit Targeting Pack und 4.5 "nativ" nutzbar. (Ja, @Samur Aran:, das ist ein Nachteil. ;))
    Dafür:
    - Skalierbarer, wenn richtig verwendet (besonders auf Server-Multicore-Prozessoren)
    - Ist in der WinRT (die neue API für Metro-Apps) der Standard für asynchrone Operationen; "Zukunftssicher"

    @Breadsoft: Ich würde dir den BackgroundWorker empfehlen. Der lässt sich einfach verwenden und ist auch noch aktuell.
    Von meinem iPhone gesendet

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „nikeee13“ ()

    hmm - grad der Backgroundworker ist doch die letzte Krücke. Da kann man ja nichtmal typisierte Argumente an die parallel auszuführende Methode übergeben.

    Dann lieber GenerischerDelegate.BeginInvoke().

    Jo, bisserl was wissen muß man halt, wenn man herumthreadeln will. Vor allem 2 Dinge:
    1: Das Gui darf nicht aussm Nebenthread aufgerufen werden
    2: Ein Thread kann niemals etwas zurückgeben. Weil zum Zurückgeben müsste ja auf den Thread gewartet werden, und dann wäre er ja nicht parallel.
    Ein Thread kann immer nur seine Ergebnisse weiterleiten - ob nun als Event gestaltet, als Callback, als Task-Continuation oder mit dieser listigen Await-Geschichte.

    nochmal zum BW: Alle Threading-Varianten können typisierte Argumente übermitteln, nur der BW nicht. Also der war schon Schrott, als er herausgekommen ist - ein Spielzeug für Förmchen-Klickser, die sich Threading aufs Form ziehen wollten, und die sich keine Mühe damit geben, den Form-Code einfach zu halten.
    In einer Dll hat der BW garnix verloren, es ist ja eine Component.

    Aber die brauchbareren Ansätze wurden ja genannt.
    im Anhang is mal meine Klasse zum Downloaden einer Datei.

    Mein Problem ist ja folgendes: Wenn ich bei Form_Shown den Download einer in einem anderen Thread starte, kommt gleich die Fehlermeldung wegen dem Threadübergreifenden Zugriff. Wenn ich den Thread weglasse, Reagiert die form nicht mehr.
    Ich hoffe ihr könnt mir auch verbesserungsvorschläge für meinen(wahrscheinlich schlecht geschriebenen) Code.

    (ich nutze die Forums-Funktion zum Anzeigen von Code nicht, weil das bei mir total falsch angezeigt wird.)
    Dateien
    • Download.vb

      (6,27 kB, 163 mal heruntergeladen, zuletzt: )

    Breadsoft schrieb:

    kommt gleich die Fehlermeldung wegen dem Threadübergreifenden Zugriff.
    War schon mal da, oder?

    VB.NET-Quellcode

    1. Me.Invoke() (Sub() Me.TextBox1.Text = "Bla")
    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!
    Du solltest Deine Posts ruhig mal lesen.

    RodFromGermany schrieb:

    VB.NET-Quellcode

    1. Me.Invoke() (Sub() Me.TextBox1.Text = "Bla")
    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!
    Okay danke vorher hat das mit Invoke nich geklappt jetzt aber irgendwie schon

    EDIT: Hätte aber nochmal ne Frage. Im Anhang is nochmal meine Klasse. Kann es sein das der Code das vielzu langsam runterlädt oder ob der code irgendwie schlecht geschrieben ist?
    Dateien
    • Download.vb

      (6,01 kB, 150 mal heruntergeladen, zuletzt: )

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

    In der aus dem anderen Thread aufgerufenen Prozedur machst Du Dir solch eine Invoke-Sub und packst alle entsprechenden Änderungen rein.
    Wenn das zu viel wird, machst Du Dir eine separate Sub und rufst die im Invoke auf.

    VB.NET-Quellcode

    1. Public Sub FromOtherThread()
    2. Me.Invoke() (Sub() DoInvoke())
    3. End Sub
    4. Private Sub DoInvoke()
    5. Me.TextBox.Text = "bla"
    6. ' hier alles reinpacken
    7. End Sub
    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!

    Breadsoft schrieb:

    Kann es sein das der Code das vielzu langsam runterlädt oder ob der code irgendwie schlecht geschrieben ist?
    jo, das kann sein.
    Du liest ja jedes Byte einzeln aussm ResponseStream, und schreibstes in den FileStream. jedes einzeln!

    guggemol Stream-Konzepte, wie man blockweise von einen Stream in den anneren schreibt. Also 1024 Bytes dürfens schon sein, als PufferGröße.
    Auch würde ich Using-Blöckse empfehlen, um Response, ResponseSTream, Filestream zuverlässig freizugeben.

    Auweiaweia!
    nicht nur liest du jedes Byte einzeln aus, sondern für jedes einzelne Byte sendest du das DownloadProgressChanged-Event. Das sammeln sich bei einem Megabyte schon so einige Eventse zusammen.
    Und jedes einzelne wird vmtl. per .Invoke (anstatt .BeginInvoke) in den MainThread transferiert - jo, so bremst man sich wirkungsvoll aus.

    Also auch Threading-Erfahrene bitte aufmerken:

    wichtige Information schrieb:

    bei Control.Invoke() wartet der Nebenthread, bis der Mainthread den Delegaten ausgeführt hat. In diesem Fall wird also nicht der Mainthread blockiert, sonnern grad annersrum: Es wird der NebenThread blockiert.
    Was ja auch nicht Sinn von Nebenläufigkeit sein kann.

    Control.BeginInvoke() sollte also die Standard-Variante des Invokings sein, und Control.Invoke() nur in besonderen Ausnahmefällen (grob gesagt: ungefähr nie).

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

    Danke dass du mir die tipps gegeben hast.
    Ich hab mich mal rangesetzt und ein bisschen dran gearbeitet.

    Der verbesserte Code ist nochmal im Anhang.

    Bei der For-Schleife weiß ich jetzt nicht ob die so noch richtig ist weil ich ja nicht mehr jedes byte einzeln auslese.
    Auf weiter Verbesserungen würde ich mich freuen.

    EDIT: Warum kann man HttpWebRequest nicht in einem Using-Block verwenden?
    Dateien
    • Download.vb

      (6,3 kB, 140 mal heruntergeladen, zuletzt: )