Async Await für parallelen Code

  • VB.NET
  • .NET 7–8

Es gibt 36 Antworten in diesem Thema. Der letzte Beitrag () ist von Haudruferzappeltnoch.

    Async Await für parallelen Code

    Hallo,

    ist Async Await für den synchronen Ablauf von paralellem Code ungeeignet?
    Irgendwie tu ich mich da schwer. Denn man kommt wenn man einmal drin ist im Async Await Pattern, ja nicht mehr auf einen synchronen Ablauf danach wenn man nicht alles Async macht.

    VB.NET-Quellcode

    1. Module Program
    2. Sub Main()
    3. Threading.Thread.Sleep(1000)
    4. Console.WriteLine("Start")
    5. HeavyWork()
    6. Console.WriteLine("End")
    7. End Sub
    8. Private Async Sub HeavyWork()
    9. Await Task.Run(AddressOf HeavyWork1)
    10. Await Task.Run(AddressOf HeavyWork2)
    11. End Sub
    12. Private Sub HeavyWork1()
    13. Threading.Thread.Sleep(1000)
    14. End Sub
    15. Private Sub HeavyWork2()
    16. Threading.Thread.Sleep(1000)
    17. End Sub
    18. End Module

    Damit End wirklich nach HeavyWork kommt muss HeavyWork selbst Awaited werden. Das ist in dem Beispiel natürlich nicht möglich.

    Viele Grüße
    Async/Await ist genau für parallelen Code.

    Ich verstehe deine Frage nicht ganz. Kannst du deine Frage nochmal etwas konkreter aufschreiben?
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)
    Ich möchte, dass mir die Console aufgeht, 1 Sekunde wartet, "Start" schreibt, auf die Beendigung von HeavyWork1 und HeavyWork2 wartet und dann erst "End" schreibt.
    Und zwischen Start und End schreiben muss weniger als 2 Sekunden Zeit liegen.

    Ich versteh, dass Async Await parallel kann, aber ich versteh nicht wie man das einschränkt, der bleibt im Codebeispiel parallel, das heißt die Main Sub endet und HeavyWork läuft noch.

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

    @Haudruferzappeltnoch Wie wäre es mit Parallel.For()?
    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:

    das heißt die Main Sub endet und HeavyWork läuft noch.

    Entweder so wie Rod sagt, in dem Fall einfach in der Main ​await aufrufen oder ​HeavyWork().GetAwaiter().GetResult() aufrufen.

    Bei letzterem hast du dann sogar in der Main nur synchronen Code.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)
    Async Await nicht nutzen, ist absolut klar. Deswegen die Frage ob Async Await ungeeignet ist.

    In der Main Await aufrufen ist nicht möglich; die Sub Main kann nicht Async sein. Wie in Post 1 angemerkt

    VB.NET-Quellcode

    1. Sub Main()
    2. Threading.Thread.Sleep(1000)
    3. Console.WriteLine("Start")
    4. Task.Run(AddressOf HeavyWork).GetAwaiter.GetResult()
    5. Console.WriteLine("End")
    6. Stop
    7. End Sub
    8. Private Async Sub HeavyWork()
    9. Await Task.Run(AddressOf HeavyWork1)
    10. Await Task.Run(AddressOf HeavyWork2)
    11. End Sub
    12. Private Sub HeavyWork1()
    13. Threading.Thread.Sleep(1000)
    14. End Sub
    15. Private Sub HeavyWork2()
    16. Threading.Thread.Sleep(1000)
    17. End Sub
    Tut nicht funktionieren, warum weiß ich nicht. Wartet 1 Sekunde schreibt dann sofort Start und End.

    Hier die funktionierende Variante ohne Async Await:

    VB.NET-Quellcode

    1. Module Program
    2. Sub Main()
    3. Threading.Thread.Sleep(1000)
    4. Console.WriteLine("Start")
    5. Parallel.Invoke(AddressOf HeavyWork1, AddressOf HeavyWork2)
    6. Console.WriteLine("End")
    7. Stop
    8. End Sub
    9. Private Sub HeavyWork1()
    10. Threading.Thread.Sleep(2000)
    11. Console.WriteLine("1")
    12. End Sub
    13. Private Sub HeavyWork2()
    14. Threading.Thread.Sleep(2000)
    15. Console.WriteLine("2")
    16. End Sub
    17. End Module

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

    Haudruferzappeltnoch schrieb:

    Tut nicht funktionieren, warum weiß ich nicht. Wartet 1 Sekunde schreibt dann sofort Start und End.


    Weil deine Asynchrone Funktionen ein Sub sind und du jetzt für den .GetAwaiter.GetResult() einfach einen Task drum gemacht hast. Das wartet nicht. Wenn du eine Async Funktion hast, sollte der zurückgebende Typ immer Task oder Task<> sein.

    C#-Quellcode

    1. static void Main(string[] args) {
    2. Thread.Sleep(1000);
    3. Console.WriteLine("Start");
    4. HeavyWork().GetAwaiter().GetResult();
    5. Console.WriteLine("End");
    6. }
    7. public static async Task HeavyWork() {
    8. await Task.Run(HeavyWork1);
    9. await Task.Run(HeavyWork2);
    10. }
    11. public static void HeavyWork1() {
    12. Thread.Sleep(1000);
    13. }
    14. public static void HeavyWork2() {
    15. Thread.Sleep(1000);
    16. }
    @Bluespide

    VB.NET-Quellcode

    1. Module Program
    2. Sub Main()
    3. Threading.Thread.Sleep(1000)
    4. Console.WriteLine("Start")
    5. HeavyWork.GetAwaiter.GetResult() 'HeavyWork.Wait selbes Ergebnis
    6. Console.WriteLine("End")
    7. Stop
    8. End Sub
    9. Private Async Function HeavyWork() As Task
    10. Await Task.Run(AddressOf HeavyWork1)
    11. Await Task.Run(AddressOf HeavyWork2)
    12. End Function
    13. Private Sub HeavyWork1()
    14. Threading.Thread.Sleep(1000)
    15. End Sub
    16. Private Sub HeavyWork2()
    17. Threading.Thread.Sleep(1000)
    18. End Sub
    19. End Module
    Klappt fast. 8| Das versteh ich aber mal gar nicht. Warum hat die Function kein Return? Da wird nicht mal gemeckert.
    Allerdings wartet er nun 2 Sekunden zwischen Start und End; ist also nicht parallel

    @ErfinderDesRades Ich finde in deinem Tutorial keinen Hinweis. Welches Run soll ich denn durch WhenAll ersetzen?

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

    Haudruferzappeltnoch schrieb:

    Ich finde in deinem Tutorial keinen Hinweis. Welches Run soll ich denn durch WhenAll ersetzen?
    nee, so simpel isses nicht, dass du nur ein Wörtlein austauschst und alles gut. Du musst dich schon auseinandersetzen.
    Zum Beispiel dein Problem ühaupt verstehen: Du hast mehrere Jobs, die gleichzeitig arbeiten sollen. Erst wenn alle fertig sind (WhenAll), solls weitergehen.
    Jo, entsprechend musst du programmieren
    Hier ein Hinweis aus msdn:
    Task.WhenAll Methode
    Erstellt eine Aufgabe, die abgeschlossen wird, wenn alle angegebenen Aufgaben abgeschlossen sind.
    Damit muss sich dein Problem doch lösen lassen.
    Also eiglich wie das zweite .WhenAny-Beispiel aus meim Tut. Dort gehts weiter, wenn der erste fertig ist - du aber brauchst, .WhenAll fertig sind.
    Hallo,
    du awaitest deine beiden Aufgaben in der Funktion HeavyWork() nacheinander. Natürlich dauert das insgesamt zwei Sekunden. Wenn du parallele Verarbeitung möchtest, hat EdR dir den entsprechenden Link zur Dokumentation geschickt.

    Beispiel
    Spoiler anzeigen

    C#-Quellcode

    1. ​internal class Program
    2. {
    3. static void Main(string[] args)
    4. {
    5. List<Task> tasks = new()
    6. {
    7. Task.Run(HeavyWork2),
    8. Task.Run(HeavyWork1)
    9. };
    10. Console.WriteLine("Start");
    11. Task t = Task.WhenAll(tasks);
    12. try
    13. {
    14. t.Wait();
    15. }
    16. catch (Exception ex)
    17. {
    18. Console.WriteLine(ex.Message);
    19. }
    20. Console.WriteLine("End");
    21. }
    22. static async Task HeavyWork1()
    23. {
    24. Console.WriteLine(DateTime.Now.ToLongTimeString());
    25. await Task.Delay(1000);
    26. Console.WriteLine($"{nameof(HeavyWork1)}: {DateTime.Now.ToLongTimeString()}");
    27. }
    28. static async Task HeavyWork2()
    29. {
    30. Console.WriteLine(DateTime.Now.ToLongTimeString());
    31. await Task.Delay(2000);
    32. Console.WriteLine($"{nameof(HeavyWork2)}: {DateTime.Now.ToLongTimeString()}");
    33. }
    34. }
    Eine Sekunde warten, dann nebenbei das schwere Zeug, aber sofort weiter zum Stop:

    VB.NET-Quellcode

    1. Module Program
    2. Sub Main()
    3. Threading.Thread.Sleep(1000)
    4. Console.WriteLine("Start")
    5. DoHeavyWorkButDontWaitForIt()
    6. Console.WriteLine("End")
    7. Stop
    8. End Sub
    9. Private Async Sub DoHeavyWorkButDontWaitForIt()
    10. Await Task.Run(AddressOf HeavyWork1)
    11. Await Task.Run(AddressOf HeavyWork2)
    12. End Sub
    13. Private Sub HeavyWork1()
    14. Threading.Thread.Sleep(1000)
    15. End Sub
    16. Private Sub HeavyWork2()
    17. Threading.Thread.Sleep(1000)
    18. End Sub
    19. End Module

    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.
    Leute nu lests doch einmal was ich schreibe.

    Es geht nicht darum es generell zum laufen zu kriegen, ich glaube darauf willst du hinaus @ErfinderDesRades. Das ist in Post 6 schon soweit dargestellt.
    Dies ist der WhenAll Ansatz den ich sehe. Das ist aber dasselbe wie in Post 6 da muss kein Async Await

    VB.NET-Quellcode

    1. Module Program
    2. Sub Main()
    3. Threading.Thread.Sleep(1000)
    4. Console.WriteLine("Start")
    5. Task.WhenAll(Task.Run(AddressOf HeavyWork1), Task.Run(AddressOf HeavyWork2)).Wait()
    6. Console.WriteLine("End")
    7. Stop
    8. End Sub
    9. Private Sub HeavyWork1()
    10. Threading.Thread.Sleep(1000)
    11. Console.WriteLine("1")
    12. End Sub
    13. Private Sub HeavyWork2()
    14. Threading.Thread.Sleep(1000)
    15. Console.WriteLine("2")
    16. End Sub
    17. End Module


    Nu war die Frage aber von Beginn an, ob das nicht mit Async Await -Ansatz funktioniert, wo das Async Await eben notwendig/Hauptoperator is.

    @VaporiZed Zwischen Start und End soll gewartet werden. Nur eben nicht die volle Zeit wie wenn HeavyWork1 und HeavyWork2 hintereinander synchron laufen, auch hier in Post 6 zeigt der zweite Code das gewünschte Verhalten.

    Also wenn es doch mit only Async Await gehen sollte, tut mir Leid, ich sehs nicht, vielleicht könnt ihr mirs zeigen.

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

    Also das sollte doch funktionieren. ROD hat oben schon die Parallel erwähnt.

    Freundiche Grüsse

    exc-jdbi

    C#-Quellcode

    1. public static void Main()
    2. {
    3. //Test1();
    4. Test2();
    5. Console.ReadLine();
    6. }
    7. private static void Test1()
    8. {
    9. Thread.Sleep(1000);
    10. Console.WriteLine($"{nameof(Test1)}: {DateTime.Now.ToLongTimeString()}");
    11. Console.WriteLine("Start");
    12. List<Task> tasks =
    13. [
    14. Task.Run(HeavyWork1),
    15. Task.Run(HeavyWork2)
    16. ];
    17. var plr = Parallel.ForEach(tasks, x =>
    18. {
    19. x.Wait();
    20. });
    21. //while (!plr.IsCompleted) ;
    22. Console.WriteLine("End");
    23. Console.WriteLine($"{nameof(Test1)}: {DateTime.Now.ToLongTimeString()}");
    24. Console.WriteLine();
    25. }
    26. private async static void Test2()
    27. {
    28. Thread.Sleep(1000);
    29. Console.WriteLine($"{nameof(Test2)}: {DateTime.Now.ToLongTimeString()}");
    30. Console.WriteLine("Start");
    31. List<Task> tasks =
    32. [
    33. Task.Run(HeavyWork1),
    34. Task.Run(HeavyWork2)
    35. ];
    36. //foreach (var t in tasks)
    37. //{
    38. // //t.Wait();
    39. // t.Start(); //Kann Probleme verursachen
    40. //}
    41. await Task.WhenAll(tasks);
    42. Console.WriteLine("End");
    43. Console.WriteLine($"{nameof(Test2)}: {DateTime.Now.ToLongTimeString()}");
    44. Console.WriteLine();
    45. }
    46. private static void HeavyWork1()
    47. {
    48. Console.WriteLine($"{nameof(HeavyWork1)} Before: {DateTime.Now.ToLongTimeString()}");
    49. Thread.Sleep(1000);
    50. Console.WriteLine($"{nameof(HeavyWork1)} After: {DateTime.Now.ToLongTimeString()}");
    51. }
    52. private static void HeavyWork2()
    53. {
    54. Console.WriteLine($"{nameof(HeavyWork2)} Before: {DateTime.Now.ToLongTimeString()}");
    55. Thread.Sleep(1000);
    56. Console.WriteLine($"{nameof(HeavyWork2)} After: {DateTime.Now.ToLongTimeString()}");
    57. }

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „exc-jdbi“ () aus folgendem Grund: Entkommentiert Zeile 46 - 50

    Aber Async Await und Task.WhenAll sind in diesem Code doch alle üflüssig.

    Scheint eine Besonderheit der ForEach Schleife zu sein.

    VB.NET-Quellcode

    1. Task.Run(AddressOf HeavyWork1).Wait
    2. Task.Run(AddressOf HeavyWork2).Wait
    läuft synchron

    VB.NET-Quellcode

    1. For Each t In {Task.Run(AddressOf HeavyWork1), Task.Run(AddressOf HeavyWork2)}
    2. t.Wait()
    3. Next
    läuft parallel

    Wie ist das zu erklären?

    Zum Testen vollständig

    VB.NET-Quellcode

    1. Module Program
    2. Sub Main()
    3. Test2()
    4. Console.ReadLine()
    5. End Sub
    6. Private Sub Test1()
    7. Thread.Sleep(1000)
    8. Console.WriteLine($"{NameOf(Test1)}: {Date.Now.ToLongTimeString()}")
    9. Console.WriteLine("Start")
    10. Task.Run(AddressOf HeavyWork1).Wait
    11. Task.Run(AddressOf HeavyWork2).Wait
    12. Console.WriteLine("End")
    13. Console.WriteLine($"{NameOf(Test1)}: {Date.Now.ToLongTimeString()}")
    14. Console.WriteLine()
    15. End Sub
    16. Private Sub Test2()
    17. Thread.Sleep(1000)
    18. Console.WriteLine($"{NameOf(Test2)}: {Date.Now.ToLongTimeString()}")
    19. Console.WriteLine("Start")
    20. For Each t In {Task.Run(AddressOf HeavyWork1), Task.Run(AddressOf HeavyWork2)}
    21. t.Wait()
    22. Next
    23. Console.WriteLine("End")
    24. Console.WriteLine($"{NameOf(Test2)}: {Date.Now.ToLongTimeString()}")
    25. Console.WriteLine()
    26. End Sub
    27. Private Sub Test3()
    28. Thread.Sleep(1000)
    29. Console.WriteLine($"{NameOf(Test3)}: {Date.Now.ToLongTimeString()}")
    30. Console.WriteLine("Start")
    31. HeavyWork1()
    32. HeavyWork2()
    33. Console.WriteLine("End")
    34. Console.WriteLine($"{NameOf(Test3)}: {Date.Now.ToLongTimeString()}")
    35. Console.WriteLine()
    36. End Sub
    37. Private Sub HeavyWork1()
    38. Console.WriteLine($"{NameOf(HeavyWork1)} Before: {Date.Now.ToLongTimeString()}")
    39. Thread.Sleep(1000)
    40. Console.WriteLine($"{NameOf(HeavyWork1)} After: {Date.Now.ToLongTimeString()}")
    41. End Sub
    42. Private Sub HeavyWork2()
    43. Console.WriteLine($"{NameOf(HeavyWork2)} Before: {Date.Now.ToLongTimeString()}")
    44. Thread.Sleep(1000)
    45. Console.WriteLine($"{NameOf(HeavyWork2)} After: {Date.Now.ToLongTimeString()}")
    46. End Sub
    47. End Module

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

    Haudruferzappeltnoch schrieb:

    Leute nu lests doch einmal was ich schreibe.
    Ok - offsichtlich habich ühaupt nicht verstanden, was du willst.

    Haudruferzappeltnoch schrieb:

    Nu war die Frage aber von Beginn an, ob das nicht mit Async Await -Ansatz funktioniert, wo das Async Await eben notwendig/Hauptoperator is.
    Worauf bezieht sich das "das"? Auf den Code von post#13?
    Funktioniert der?
    Wenn ja, Warum sollte man dann dort Async/Await einbringen, wenn es doch funktioniert?

    Haudruferzappeltnoch schrieb:

    ist Async Await für den synchronen Ablauf von paralellem Code ungeeignet?
    Was ist "synchroner Ablauf von paralellem Code"? also in meiner Welt läuft Code entweder synchron oder paralell.
    Bzw der einzige Sinn, den ich da reininterpretieren konnte war die Geschichte mit .WhenAll, aber das war iwie falsch
    Worauf bezieht sich das "das"? Bezieht sich auf "synchroner Ablauf von paralellem Code" (siehe unten)

    Funktioniert der [Code Post 13]? Ja, in Post 6 auch schon.

    Wenn ja, Warum sollte man dann dort Async/Await einbringen, wenn es doch funktioniert? Jo sollte man nicht. Es geht drum, ist das durch Async Await irgendwie ersetzbar (nicht ergänzbar)?

    Was ist "synchroner Ablauf von paralellem Code"?
    In meinem Kopf unterscheiden sich folgende Codeabläufe:

    Die rechte Seite ist zum Beispiel, dass die GUI nicht friert wenn man Knöppe drückt. (Die GUI läuft auch ohne asynchrone Buttons)
    Die linke Seite lädt zum Beispiel Daten. (Ohne Input ist der Algorithmus nicht funktional.)

    Wie man es korrekt formulieren muss, wirst du mir sicher verraten. Für mich ist der parallele Code rechts asynchron und links synchron.

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

    Die Task.Wait Methode stellt schon sicher, dass der Task abgeschlossen wird. Async-Await muss für diese Methode alleine nicht verwendet werden. Siehe hier.
    learn.microsoft.com/de-de/dotn…threading.tasks.task.wait

    Async-Await sind nur sinnvoll wenn Methoden verwendet werden die Avaitable sind, wie z.B. Task.WhenAll.

    johnthiriet.com/removing-async-void/


    Freundliche Grüsse

    exc-jdbi

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „exc-jdbi“ ()

    Haudruferzappeltnoch schrieb:

    Was ist "synchroner Ablauf von paralellem Code"?
    Dann hab ich dich ja doch richtig verstanden, und bereits geantwortet: .WhenAll ist dein Froind.

    Aber du solltest nicht mit einer Console experimentieren, sondern mit einem Winform. Da kannst du etwas bauen, was beide Prozesse nebenläufig anstösst, ohne die Gui zu blockieren.
    Und .WhenAll fertig poppt eine Meldung dazu auf: "alle fertig".

    Und im Tut ist fast gezeigt, wies gemacht wird. Nur da poppt die Meldung nicht .WhenAll auf, sondern bereits .WhenAny.

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

    @ErfinderDesRades
    Du hast es vielleicht bereits geantwortet aber offensichtlich nicht auf die Fragen, die ich frug. Ich will kein Problem lösen, ich will was verstehen.
    Ich nehme erstmal mit, dass Async Await unbrauchbar für Fall links ist.

    @exc-jdbi Für meinen Beispielfall muss ich WhenAll aber überhaupt nicht Awaiten auch wenn es Awaitable is. Das verhält sich von allein schon wie geplant.

    Kann ich nochmal auf Post 15 verweisen? Ich hätte angenommen, dass sich beide Fälle gleich Verhalten. Was muss man da noch beachten?