Async Await für parallelen Code
- VB.NET
- .NET 7–8
Sie verwenden einen veralteten Browser (%browser%) mit Sicherheitsschwachstellen und können nicht alle Funktionen dieser Webseite nutzen.
Hier erfahren Sie, wie einfach Sie Ihren Browser aktualisieren können.
Hier erfahren Sie, wie einfach Sie Ihren Browser aktualisieren können.
Es gibt 36 Antworten in diesem Thema. Der letzte Beitrag () ist von Haudruferzappeltnoch.
-
-
Jo. Ich habe mich auch schon wieder länger nicht mit dieser async-await Geschichte befasst.
Das wäre für mich irgendwie die richtige und logische Vorgehensweise.
Spoiler anzeigen C#-Quellcode
- internal partial class Program
- {
- public static void Main()
- {
- //TestSeq();
- TestParallelAsyncAwait();
- //TestParallelOnlyTask();
- //TestParallelForEach();
- Console.ReadLine();
- }
- private static void TestSeq()
- {
- //Sequenzieller Ablauf.
- Thread.Sleep(1000);
- Console.WriteLine($"{nameof(TestSeq)}: {DateTime.Now.ToLongTimeString()}");
- Console.WriteLine("Start");
- //sequenzieller Ablauf ohne Rücksetzung (using) der Task.
- //Vorgehensweise wird oft verwendet, ist aber streng genommen
- //nicht die richtige, weil für die sequentielle kein Task
- //gebraucht wird.
- //Task.Run(HeavyWork1).Wait();
- //Task.Run(HeavyWork2).Wait();
- HeavyWork1();
- HeavyWork2();
- Console.WriteLine("End");
- Console.WriteLine($"{nameof(TestSeq)}: {DateTime.Now.ToLongTimeString()}");
- Console.WriteLine();
- }
- private async static void TestParallelAsyncAwait()
- {
- //Alle Task laufen Parallel mit async await
- Thread.Sleep(1000);
- Console.WriteLine($"{nameof(TestParallelAsyncAwait)}: {DateTime.Now.ToLongTimeString()}");
- Console.WriteLine("Start");
- using var t1 = Task.Run(HeavyWork1);
- using var t2 = Task.Run(HeavyWork2);
- // .. oder wenn z.B. CancellationToken verwendet wird.
- //await t1.WaitAsync();
- //await t2.WaitAsync();
- //... oder so
- //var tasks = new List<Task> { t1, t2 };
- //await Task.WhenAll(tasks);
- //... oder so
- await Task.WhenAll(t1, t2);
- Console.WriteLine("End");
- Console.WriteLine($"{nameof(TestParallelAsyncAwait)}: {DateTime.Now.ToLongTimeString()}");
- Console.WriteLine();
- }
- private static void TestParallelOnlyTask()
- {
- //Alle Task laufen Parallel OHNE async await
- Thread.Sleep(1000);
- Console.WriteLine($"{nameof(TestParallelOnlyTask)}: {DateTime.Now.ToLongTimeString()}");
- Console.WriteLine("Start");
- using var t1 = Task.Run(HeavyWork1);
- using var t2 = Task.Run(HeavyWork2);
- //mit jeweiligen Wait() ...
- //t1.Wait(); t2.Wait();
- //..Alle Wait() einfach über eine foreach
- //Task[] tasks = [ t1, t2 ];
- //foreach (var t in tasks)
- // t.Wait();
- //.. oder die schönste vorgehensweise
- Task.WaitAll(t1, t2);
- Console.WriteLine("End");
- Console.WriteLine($"{nameof(TestParallelOnlyTask)}: {DateTime.Now.ToLongTimeString()}");
- Console.WriteLine();
- }
- private static void TestParallelForEach()
- {
- //Alle Task laufen Parallel OHNE async-await
- Thread.Sleep(1000);
- Console.WriteLine($"{nameof(TestParallelForEach)}: {DateTime.Now.ToLongTimeString()}");
- Console.WriteLine("Start");
- using var t1 = Task.Run(HeavyWork1);
- using var t2 = Task.Run(HeavyWork2);
- Task[] tasks = [t1, t2];
- //Da Task die 'Task.WhenAll' besitzt,
- //macht die Parallel.ForEach keinen Sinn.
- //Möglich ist die vorgehensweise aber.
- var plr = Parallel.ForEach(tasks, t =>
- {
- t.Wait();
- });
- //while (!plr.IsCompleted) ;
- Console.WriteLine("End");
- Console.WriteLine($"{nameof(TestParallelForEach)}: {DateTime.Now.ToLongTimeString()}");
- Console.WriteLine();
- }
- private static void HeavyWork1()
- {
- Console.WriteLine($"{nameof(HeavyWork1)} Before: {DateTime.Now.ToLongTimeString()}");
- Thread.Sleep(1000);
- Console.WriteLine($"{nameof(HeavyWork1)} After: {DateTime.Now.ToLongTimeString()}");
- }
- private static void HeavyWork2()
- {
- Console.WriteLine($"{nameof(HeavyWork2)} Before: {DateTime.Now.ToLongTimeString()}");
- Thread.Sleep(1000);
- Console.WriteLine($"{nameof(HeavyWork2)} After: {DateTime.Now.ToLongTimeString()}");
- }
- }
Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „exc-jdbi“ () aus folgendem Grund: Bisserl ausgehübst
-
-
Der Code in Post#15 ergibt, nach Ergänzung, dass in Test1 die Tasks - entsprechend Wait - nacheinander abgearbeitet werden, in Test 2 gleichzeitig. Ok, da ist jetzt unklar, warum. Es macht ja quasi das gleiche. Ob jetzt
oderIch verstehe das Problem langsam.
##########
Das führt zum gleichen Ergebnis wie die For-Schleife. Beide Tasks werden gleichzeitig gestartet (also nicht nur vorbereitet) undB.Wait
wird erreicht, wenn ein Task fertig 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.Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()
-
-
Haudruferzappeltnoch schrieb:
Task.Run(AddressOf HeavyWork1).Wait
Task.Run(AddressOf HeavyWork2).Wait
Haudruferzappeltnoch schrieb:
For Each t In {Task.Run(AddressOf HeavyWork1), Task.Run(AddressOf HeavyWork2)}
t.Wait()
Next
sind leider nicht das gleiche weil in der foreach wird eine Array<Task> abgearbeitet, und in der oben ist es die Wait-Methode, also eine void-Methode die nacheinander aufgerufen wird ohne vorher je ein Task-Datentyp erzeugt zu haben.
SoTask.Run(AddressOf HeavyWork2).Wait
wird also nie vorab ein Task-Datentyp erstellt. Der ist aber anscheinend notwending, um alle Wait-Methoden zusammenzuführen um es nachher Parallel wie es in der Foreach gemacht wird auszuführen.
Gruss exc-jdbi -
In zeile#2 wird zunächstmal auf Task1 gewartet.
Wenn er durch ist wird direkt danach auf Task2 gewartet. Der ist aber schon die ganze Zeit gelaufen, und deshalb ist die Rest-Wartezeit natürlich kürzer.
Bzw. wenn Task2 schon fertig ist, wenn auf ihn gewartet werden soll ist die Rest-Wartezeit 0.
Insgesamt wird also bei dieser Konstruktion auf den langsamsten der beiden Tasks gewartet, egal obs Task1 ist oder Task2.
-
Dank der Erkenntnisse, die ihr mir vermitteln konntet, habe ich nun die Async/Await Version gefunden (also kein Run, WhenAny, WhenAll):
VB.NET-Quellcode
- Module Program
- Sub Main()
- HeavyWorkReihe()
- HeavyWorkParallel()
- Console.ReadLine()
- End Sub
- Private Sub HeavyWorkReihe()
- Thread.Sleep(1000)
- Console.WriteLine($"{NameOf(Test1)}: {Date.Now.ToLongTimeString()}")
- Console.WriteLine("Start")
- HeavyWork1()
- HeavyWork2()
- Console.WriteLine("End")
- Console.WriteLine($"{NameOf(Test1)}: {Date.Now.ToLongTimeString()}")
- Console.WriteLine()
- End Sub
- Private Sub HeavyWorkParallel()
- Thread.Sleep(1000)
- Console.WriteLine($"{NameOf(Test2)}: {Date.Now.ToLongTimeString()}")
- Console.WriteLine("Start")
- Dim A = HeavyWork1Async()
- Dim B = HeavyWork2Async()
- A.Wait()
- B.Wait()
- Console.WriteLine("End")
- Console.WriteLine($"{NameOf(Test2)}: {Date.Now.ToLongTimeString()}")
- Console.WriteLine()
- End Sub
- Private Sub HeavyWork1()
- HeavyWork1Async.Wait()
- End Sub
- Private Sub HeavyWork2()
- HeavyWork2Async.Wait()
- End Sub
- Private Async Function HeavyWork1Async() As Task
- Console.WriteLine($"{NameOf(HeavyWork1)} Before: {Date.Now.ToLongTimeString()}")
- Await Task.Delay(1000)
- Console.WriteLine($"{NameOf(HeavyWork1)} After: {Date.Now.ToLongTimeString()}")
- End Function
- Private Async Function HeavyWork2Async() As Task
- Console.WriteLine($"{NameOf(HeavyWork2)} Before: {Date.Now.ToLongTimeString()}")
- Await Task.Delay(1000)
- Console.WriteLine($"{NameOf(HeavyWork2)} After: {Date.Now.ToLongTimeString()}")
- End Function
- End Module
Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()
-
jo, und nu probierma so:
VB.NET-Quellcode
- Private Async Sub HeavyWorkParallel2()
- Thread.Sleep(1000)
- Console.WriteLine($"{NameOf(HeavyWorkParallel2)}: {Date.Now.ToLongTimeString()}")
- Console.WriteLine("Start")
- Await Task.WhenAll(HeavyWork1Async(), HeavyWork2Async())
- Console.WriteLine("End")
- Console.WriteLine($"{NameOf(HeavyWorkParallel2)}: {Date.Now.ToLongTimeString()}")
- Console.WriteLine()
- End Sub
-
Funktioniert, aber hier kommt jetzt der Punkt den ich immer noch nicht verstehe:
Das funktioniert auch:
VB.NET-Quellcode
- Private Sub HeavyWorkParallel2()
- Thread.Sleep(1000)
- Console.WriteLine($"{NameOf(HeavyWorkParallel2)}: {Date.Now.ToLongTimeString()}")
- Console.WriteLine("Start")
- Task.WhenAll(HeavyWork1Async(), HeavyWork2Async()).Wait()
- Console.WriteLine("End")
- Console.WriteLine($"{NameOf(HeavyWorkParallel2)}: {Date.Now.ToLongTimeString()}")
- Console.WriteLine()
- End Sub
Und ich finde es auch ganz unintuitiv, dass ich aus dem Ausführen von Tasks wieder eine neue Task mache, was die WhenAll, Run, etc. Methoden ja tun. Ist das nicht doppelt gemoppelt? -
Await Task
entsprichtTask.Wait
.Await
kam einfach syntaktisch später.
##########
hab's nicht komplett gelesen, aber hier ein Artikel dazu
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.Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()
-
Das ist genau der Punkt wo ich Unterschiede zwischen Consolenanwendungen oder Windowsprogrammen, obwohl ich diese Unterscheidung nicht unbedingt gerne mache.
Das von EDR in #29 gezeigte Beispiel funkst nämlich in einem Windowsprogramm, weil dort das Fenster nach dem aufgebaut konstant auf dem Bildschirm gezeigt wird.
In der Consolenanwendung funkst das nur, wenn ein Console.ReadLine() oder etwas dergleichen vorliegt. Ich stell mir das immer vor, wie ein sehr grosser Winderstand der das Ausführen von noch nicht ausgeführten Task zwingt, endlich ausgeführt zu werden. (Ähnlich wie DoEvent).
Ein weiteres Vorteil den ich im Zusammenhang mit async-await nutzen will, ist die Entscheidung, wann und vorallem wo, ein Task komplett fertig ausgeführt wird. Da Void-Methoden keinen Rückgabewert haben, kann ich als Entwickler nicht direkt entscheiden wo und wann der Task komplett fertig ausgeführt wird. Die Ausführung muss nämlich in der entsprechenden Void-Methode direkt gemacht werden. (ausser man arbeitet mit Fenster dann kann wie der Code von EDR zeigt der Task auch so zum direkten Ausführen gebracht werden, dass funkst dann mit der deklaration von await, und die Wait()-Methode muss nicht mehr erwähnt werden.)
EDIT: Wer sich Async/Await in Anwendung einer Console anschauen will, ich habe hier das Generieren von Primes so umgesetzt.
Power Fast Prime GeneratorDieser Beitrag wurde bereits 2 mal editiert, zuletzt von „exc-jdbi“ ()
-
VaporiZed schrieb:
Await Task
entsprichtTask.Wait
.Await
kam einfach syntaktisch später.
Await returnt die Async-Methode vorzeitig.sodasss der MainThread nicht blockiert.
Wenn der NebenThread dann durch ist, springt er wieder an die Await-Stelle und macht danach weiter.
Kaum zu glauben, schwer zu verstehen, aber in Winforms ungemein praktisch.
Hier isses richtig erklärt: codeproject.com/Articles/10296…ithout-any-additional-Lin -
-
Folgende Schleife:
Also die Requests laufen anständig synchron und die Destinations parallel.
-
Haudruferzappeltnoch schrieb:
gibt es da was dran zu verbessern?
Lass das Dispose noch weg, Tasks müssen nicht Disposed werden:
Do I need to dispose of Tasks?
Here’s my short answer to this question:
“No. Don’t bother disposing of your tasks.” -
-
Benutzer online 1
1 Besucher
-
Ähnliche Themen
-
9 Benutzer haben hier geschrieben
- Haudruferzappeltnoch (14)
- ErfinderDesRades (8)
- exc-jdbi (5)
- VaporiZed (3)
- siycah (2)
- Bluespide (2)
- RodFromGermany (1)
- ISliceUrPanties (1)
- Amro (1)