Unterschied zwischen Benutzung von Async Sub und Async Task(of Type)

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

Es gibt 28 Antworten in diesem Thema. Der letzte Beitrag () ist von kafffee.

    Unterschied zwischen Benutzung von Async Sub und Async Task(of Type)

    Hallo ;)

    ich habe folgende Async Sub:

    VB.NET-Quellcode

    1. Private Async Sub GetCoverArtFromCoverArtPfad(Pfad As String, URLIsValid As Boolean)
    2. If System.IO.File.Exists(Pfad) Then
    3. BildZwischenspeicherListe.Clear()
    4. BildZwischenspeicherListe.Add(New MemoryStream(System.IO.File.ReadAllBytes(Pfad)))
    5. BildIndex = 0
    6. Dim NeuesCoverArt As System.Drawing.Image = System.Drawing.Image.FromStream(BildZwischenspeicherListe(BildIndex))
    7. CoverArt = BitmapToImageSource(New Bitmap(NeuesCoverArt))
    8. ElseIf URLIsValid Then
    9. Try
    10. BildZwischenspeicherListe.Clear()
    11. BildZwischenspeicherListe.Add(Await GetCoverFromWeb(Pfad)) 'hier wir mit dem HttpClient ein Bild downgeloadet
    12. BildIndex = 0
    13. Dim NeuesCoverArt As System.Drawing.Image = System.Drawing.Image.FromStream(BildZwischenspeicherListe(BildIndex))
    14. CoverArt = BitmapToImageSource(New Bitmap(NeuesCoverArt))
    15. Catch ex As Exception
    16. CoverArt = Nothing
    17. BildIndex = -1
    18. End Try
    19. Else
    20. CoverArt = Nothing
    21. BildIndex = -1
    22. End If
    23. RaisePropertyChanged(NameOf(WelchesBild))
    24. End Sub


    Funktioniert auch soweit, bloss wenn ich das jetzt anstatt von Sub als Async Function GetCoverArtFromCoverArtPfad() As Task(Of ImageSource) machen will, und folglich alle Vorkommen von von CoverArt = durch Return ersetze, dann friert mein Programm ein in der GetCoverFromWeb()

    GIbt es Unterchiede in der Benutzung von Async Subs und Async Functions As Task (of Type)

    Ich kann mir nicht erklären was ich falsch mache....?
    Was ist der Aufrufkontext von GetCoverArtFromCoverArtPfad
    Ist GetCoverFromWeb selbst ein Task(Of Irgendwas)?
    Die Bedeutung verändert sich dadurch natürlich.
    Was passiert bei

    VB.NET-Quellcode

    1. Methode1()
    2. GetCoverArtFromCoverArtPfad() 'Get… wäre die falsche Benennung für eine Sub, da Get eine Wertrückgabe impliziert
    3. Methode2()

    Es werden alle Methoden nacheinander aufgerufen. Aber sobald in GetCoverArtFromCoverArtPfad() das Await erreicht wird, geht es mit Methode2 weiter.
    Wenn Du aber ne Function draus machst, bekommst Du einen Task wieder, den Du per Await auch aufrufen musst.

    VB.NET-Quellcode

    1. Methode1()
    2. Await GetCoverArtFromCoverArtPfad()
    3. Methode2()

    Aber dann wird auch auf GetCoverArtFromCoverArtPfad gewartet, bevor es mit Methode2 weitergeht.
    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.

    VaporiZed schrieb:

    Was ist der Aufrufkontext von GetCoverArtFromCoverArtPfad


    Das wird aus einem Getter einer Property heraus aufgerufen:

    VB.NET-Quellcode

    1. Public Property CoverArtInFiles As List(Of ImageSource)
    2. Get
    3. Return Await GetCoverArtFromCoverArtPfad(CoverArtPfad, result)
    4. End Get
    5. [...]
    6. End Property


    Wenn ich da, wie hier Await vorsetze, streicht er mir das rot an. Also hab ich mal rumexperimentiert und das so gemacht:

    VB.NET-Quellcode

    1. Return GetCoverArtFromCoverArtPfad(CoverArtPfad, result).GetAwaiter.GetResult


    Aber da bleibt er auch hängen. Da lag ich wohl falsch in der Annahme, dass mit .GetAwaiter.GetResult asynchrone Methoden aus einer nicht asynchronen Methode aufgerufen werden können.


    VaporiZed schrieb:

    Ist GetCoverFromWeb selbst ein Task(Of Irgendwas)?


    VB.NET-Quellcode

    1. Private Async Function GetCoverFromWeb(URL As String) As Task(Of MemoryStream)
    2. Dim Res = Await Klient.GetAsync(URL)
    3. Return CType(Await Res.Content.ReadAsStreamAsync(), MemoryStream)
    4. End Function
    Ich verstehe es so.
    Wenn man Await vor den Aufruf einer Methode schreibt, dann ist eine Async Sub dasselbe wie eine Async Function as Task, so eine Funtion returnt dann nämlich nichts.
    Eine Async Function as Task(Of T) returnt auch T wenn sie Awaitet wird.

    Werden diese Methoden nicht Awaited, dann ändert sich das Verhalten. Die Async Sub fährt los und arbeitet im Nirvana.
    Die Async Function as Task returnt dann genauso wie die Async Function as Task(Of T) eine Task. Und das heißt da läuft eigentlich nur der Teil des Codes, der vor der Task läuft die indirekt durch das Await returnt wird.
    Die Tasks müssen dann ja erstmal gestartet werden.
    Und nu weiß ich nicht was passiert, wenn die nicht gestartet werden.

    Vielleicht erklärst du was passieren soll.

    VB.NET-Quellcode

    1. Public Property CoverArtInFiles As List(Of ImageSource)
    2. Get
    3. Return Await GetCoverArtFromCoverArtPfad(CoverArtPfad, result)
    4. End Get
    5. [...]
    6. End Property
    Das streicht der rot an weil die Get-Methode nicht Async ist. Denk auch nicht, dass das geht.
    Aber als die Methode noch eine Sub war, kann das wohl kaum der Aufruf dieser Methode gewesen sein.

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

    Haudruferzappeltnoch schrieb:

    Vielleicht erklärst du was passieren soll.

    Ich möchte halt, dass im Getter meiner Property das Bild aus dem Internet geladen wird und in die Property geschrieben wird.


    Haudruferzappeltnoch schrieb:

    Denk auch nicht, dass das geht.

    Ja also was ich probiert hab, so eine Art asynchrone Relais-Methode zu schreiben. Also die wird vom Getter aus aufgerufen und ruft dann ihrerseits wieder GetCoverArtFromPfad auf:

    VB.NET-Quellcode

    1. Private Async Function CARelay(argPfad As String, argURLISValid As Boolean) As Task(Of ImageSource)
    2. Return Await GetCoverArtFromCoverArtPfad(argPfad, argURLISValid)
    3. End Function


    Haudruferzappeltnoch schrieb:

    Aber als die Methode noch eine Sub war, kann das wohl kaum der Aufruf dieser Methode gewesen sein.


    Jou, genau. Also statt mit dem Return den Wert zurückzugeben, hab ich den in einer Private Variable gespeichert.

    Hat jemand ne Idee wie ich das lösen könnte? Das muss doch irgendwie gehen...
    Okay muss nicht asynchron sein. Wie sonst, ich hab ja letztenendes zumindest die ReadAsStreamAsync? Wie würdest du es machen?
    ---------------
    Gar keins, ich kann das ja auch einfach in ner Variablen hinterlegen, leider muss die dann ja auch glaub ich ByVal übergeben werden, ByRef geht bei asynchronen Methoden ja glaub ich nicht.

    Aber es wär halt eleganter und das Thema Asnc/Await hab ich nur oberflächlich verstanden, wie man sieht. Interessiert mich also eher generell, aber ihr habt das ja in diesem Thread schon soweit gut erklärt. Bloss das hab ich mich schon länger gefragt, wie man das in genau so einem Fall wie dem hier machen kann.
    Und wann man jetzt das besagte GetAwaiter.GetResult anwendet oder wann z.B. auch dieses ConfigureAwait(False) das hab ich jetzt auch schon öfter gesehen...
    GetAwaiter hab ich noch nie genutzt.

    ConfigureAwait da biste auch schon im fortgeschrittenen Bereich von Async Await. Das ist glaub ich sogar ein relativ junges Feature.

    kafffee schrieb:

    Wie würdest du es machen?

    Na wahrscheinlich ungefähr so wie du auch im ersten Post. Wenns funktioniert, dann funktionierts. Ansonsten wie gesagt, ist nicht klar "was genau" überhaupt machen.
    Was davon tut Post 1 nicht? Und pack zu Post 1 noch den Aufruf, wann läuft das überhaupt? Auch in der Property? Das wäre dann der Größte Unterschied zwischen deinem und meinem Ansatz.

    Eine Property soll den Wert im Allgemeinen nicht immer neu holen, wenn die Property abgegriffen wird. Und genau sowas täte ja die Pseudocode-Variante von Post 3.
    Wenn da zwei verschiedene Sachen gleichzeitig diese Property wissen wollen, dann wird zweimal das Bild geholt.
    Für mich hört sich das Ganze allgemein nicht mal veränderlich an. Das Bild muss ein einziges Mal geholt werden. Und dann kann ich die Property dazu 100-mal abrufen.
    Und dann hol ich vielleicht irgendwann mal ein anderes Bild.
    Yep, den Aufruf findeste in Post#3. Also vom Getter der Property aus. Das mit dem 1000 mal aufrufen ist klar, da ist etwas geplant wie:

    VB.NET-Quellcode

    1. Private _CoverArtInFiles As List(Of ImageSource)
    2. Public Property CoverArtInFiles As List(Of ImageSource)
    3. Get
    4. If _CoverArtInFiles IsNot Nothing Then
    5. Return _CoverArtInFiles
    6. End If
    7. Return Await GetCoverFromCoverArtPfad(..., ...)
    8. End Get
    9. [...]


    Falls jemand doch noch Licht ins Dunkel bringen kann, gerne auch noch eure Vorschläge Posten...
    @Haudruferzappeltnoch
    Wir reden bestimmt aneinander vorbei. Wieso meinst du das?

    ErfinderDesRades schrieb:

    die Property müsste einen Async-Getter haben, wenn das gehen soll.

    Denke nicht dass das .NET überhaupt zulässt. Habs jedenfall probiert...

    ErfinderDesRades schrieb:

    und dann muss man den Getter immer mit Await aufrufen.

    Schwierig. Ist per Binding an die View gebunden.

    Ich hab mal gegoogelt und mich schon gefreut, dass es so viele Lösungsansätze gibt:

    stackoverflow.com/questions/66…d-from-a-getter-or-setter

    Hab jetz drei Stunden lang fast jede der angebotenen Lösungen ausprobiert, aber keine funktioniert, das scheitert teilweise bereits zur Designzeit, dass er mir gleich wieder was rot unterstreicht...

    Und dass das Ganze funktioniert, genügt es nicht, statt der Asnyc Funktion in eine Async Sub zu machen, da hab ich mich leider in Post#1 vertan. Denn da bekomm ich Nothing zurück, wenn ich den Wert direkt nach der Sub aus der Variable auslese... Irgendwie auch logisch.

    Jetzt bin ich echt aufgeschmissen. Hat jemand noch ne Idee? Vielleicht schaut ihr euch den Link mal an und habt einen Einfall...
    Anstatt so ein gefummel, warum nicht einfach ein Event nutzen? Zutaten vom Koch zubereiten lassen, kanner im Hintergrund in der Küche machen, stellt dem Kellner das Gericht hin wenn fertig, der Kellner bringt es dir und du feierst dann das Event Gaumenschmaus und hast alles was du brauchst am Tisch.


    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D
    Ich bin anderer Meinung. Aber als VBler erkennt man das nicht wenn man keine Sprachen mit C-Syntax beherscht, in C# ist das deutlicher/besser zu erkennen. (Weil in VB nicht "function XXX as Sub" geschrieben wird), bei C-Style steht nachdem AccessModifier immer der ReturnType.

    learn.microsoft.com/en-us/dotn…erence/builtin-types/void

    void(in VB Sub) ist ein Return-Type, der kommt dann zurück, dieser Typ kann aber keinen Wert annehmen.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

    Haudruferzappeltnoch schrieb:

    Werden diese Methoden nicht Awaited, dann ändert sich das Verhalten. Die Async Sub fährt los und arbeitet im Nirvana.

    Genau, der läuft dann einfach asynchron los und wird nie synchronisiert. Als würdest du quasi einfach nen Thread starten.

    Haudruferzappeltnoch schrieb:

    Das ist glaub ich sogar ein relativ junges Feature.

    Das gibt es tatsächlich schon recht lang. Erinnere mich noch an die Anfangszeiten von TAP - da war das schon in den damaligen MSDN-Docs auch gelistet und wurde verwendet.
    Wichtig ist aber ConfigureAwait(False) eigentlich nie zu nutzen, wenn man nicht explizit weiß, was man tut. Das sorgt nämlich dafür, dass die Methodenausführung nach dem Await nicht mehr im aktuellen Synchronisierungskontext (z.B. hier UI-Thread) läuft, sondern irgendeinen verfügbaren Thread im Pool verwendet. Das ist ok, wenn man danach im UI-Thread nichts mehr machen muss, aber sonst bricht das.

    kafffee schrieb:

    GIbt es Unterchiede in der Benutzung von Async Subs und Async Functions As Task (of Type)
    Ich kann mir nicht erklären was ich falsch mache....?

    Nein. Nur dass der Task etwas zurückgibt, was nicht void/Sub ist. In einem normalen Async Task beendet ein Return die Ausführung. Im Async Task(Of T) auch, aber es wird halt noch ein Wert mitgegeben (z.B. Return 1. Handhabung ist aber genau gleich.

    Hinweis: Nur weil du async-await benutzt heißt das noch nicht, dass die Methode automatisch asynchron ist. Sie wird nur an den Await-Stellen sozusagen aufgeschnitten und in verschiedene Aufrufteile unterteilt. Das ist ein Syntactic Sugar, damit man nicht Task.ContinueWith-Ketten bauen muss und die gewohnte Methodenstruktur beibehalten kann. Wenn das ganze nur in Teilen in anderen Threads läuft, rennt der Rest dir trotzdem auf dem UI-Thread bzw. im originalen Synchronisierungskontext, von dem aus aufgerufen wurde. An irgendeiner Stelle muss die asynchrone Arbeit auch wirklich auf einen neuen Thread ausgelagert werden. Erst dann wird der aktuelle (UI-)Thread entblockt, der Aufgabencode läuft asynchron/parallel und die Methodenausführung wird erst fortgeführt, sobald der Task durch ist. Dann bist du wieder im blockenden Bereich.
    Um selbst so einen asynchronen Task zu bauen, muss man ihn mit Task.Run erstellen und dann zurückgeben. Der ist dann für andere Methoden awaitbar. Die bauende Methode muss aber selbst nicht Async markiert sein, sondern nur den Return-Type Task oder Task(Of T) haben. Async-Keyword brauchst du nur, wenn du im Methoden-Body aktiv awaiten willst.

    Properties sind Stand jetzt nicht als Async markierbar. Das geht nur für Methoden und Lambdas.
    Um dir zu helfen, benötigen wir am besten mal einen kompletten Code aller relevanten Methoden, die hier irgendwie awaited werden. Teile deines Codes können so nicht funktionieren, weil sie einfach nicht möglich sind und so ist es schwierig, da richtig einzutauchen. Schätze hier werden noch verschiedene Ansätze miteinander vermischt und dann kriegst du komische Ergebnisse, bei denen du selbst nicht weißt, was du erwarten würdest. :D

    Viele Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:

    Dieser Beitrag wurde bereits 10 mal editiert, zuletzt von „Trade“ ()

    kafffee schrieb:


    ErfinderDesRades schrieb:

    und dann muss man den Getter immer mit Await aufrufen.

    Schwierig. Ist per Binding an die View gebunden.
    Ich denke, es geht eben nicht, was du vorhast.
    Async/Await verhält sich sehr speziell.
    zunächstmal wenn eine async-Methode nicht await aufgerufen wird, so ist sie nicht nebenläufig - also sinnlos.
    Dann: die awaitende Methode returnt vorzeitig!
    Sie returnt am Await-Schlüsselwort.
    Später, wenn die asynce methode durch ist, springt der Programm ablauf wieder hinter die Await-Stelle und setzt (mit den Daten) fort.
    die awaitende Methode returnt also zweimal.
    Also imo gehts nicht, an eine asynce Property zu binden.

    Das Asynce muss annerswo laufen in der Viewmodel-logik.

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

    ErfinderDesRades schrieb:

    zunächstmal wenn eine async-Methode nicht await aufgerufen wird, so ist sie nicht nebenläufig
    Doch, nebenläufig ist der Task schon, wenn er entsprechend an einer Stelle Arbeit auf einen neuen Thread auslagert. Aber es gibt halt dann kein Warten darauf, sondern der Rest der aufrufenden Methode läuft einfach direkt weiter. Also ja, sinnlos ist es natürlich, weil das Pattern dann keinen Sinn macht und man auch einfach so einen Thread auf den Pool legen kann.

    ErfinderDesRades schrieb:

    Also imo gehts nicht, an eine asynce Property zu binden.

    Das Asynce muss annerswo laufen in der Viewmodel-logik.
    Ja. Ist eigentlich relativ blöd, weil es keinen technischen Grund gibt, asynchrone Properties nicht zu erlauben. Ist halt semantisch wohl nicht so sinnvoll definiert. Daher geht das nur über eine asynchrone Methode, die das macht und dann den Wert an der Ziel-Property, die gebunden wird, setzt. Bis dahin muss die Property halt irgendeinen Default-Wert (z.B. default(T) oder halt null, wenn Nullable) halten. Die Methode sollte halt direkt einen CompletedTask zurückgeben, wenn sie schon gelaufen ist oder sie setzt halt nochmal neu (je nach Use-Case). Im Endeffekt macht eine Property intern ja auch nichts anderes, als eine Getter-Funktion auszuführen.

    Viele Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    wie gesagt, mir macht das Binden an eine Async-Property keinen Sinn. Das Binding braucht die Daten sofort - es muss sie ja anzeigen.
    Es kann ja nicht warten, und solange "nicht" anzeigen.
    Oder anders: Das Binding reagiert ja auf PropertyChanged. Ein PropertyChanged wäre aber verfrüht, wenn die Daten noch garnet da sind.

    (my 5 ct - will mich da nu nich festbeissn)