Kann jemand im folgenden Beispiel erklären, ob await
und ContinueWith
sind oder nicht. Ich versuche zum ersten Mal, TPL zu verwenden und habe die gesamte Dokumentation gelesen, verstehe aber den Unterschied nicht.
Warten Sie :
String webText = await getWebPage(uri);
await parseData(webText);
Weiter mit :
Task<String> webText = new Task<String>(() => getWebPage(uri));
Task continue = webText.ContinueWith((task) => parseData(task.Result));
webText.Start();
continue.Wait();
Wird in bestimmten Situationen das eine dem anderen vorgezogen?
c#
task-parallel-library
task
async-await
Harrison
quelle
quelle
Wait
Anruf in dem zweiten Beispiel dann die beiden Schnipsel wäre (meist) gleichwertig.getWebPage
Methode kann nicht in beiden Codes verwendet werden. Im ersten Code hat es einenTask<string>
Rückgabetyp, während es im zweiten Code einen Rückgabetyp hatstring
. Ihr Code wird also im Grunde nicht kompiliert. - wenn um genau zu sein.Antworten:
Im zweiten Code warten Sie synchron auf den Abschluss der Fortsetzung. In der ersten Version kehrt die Methode zum Aufrufer zurück, sobald sie den ersten
await
Ausdruck trifft, der noch nicht abgeschlossen ist.Sie sind sich insofern sehr ähnlich, als sie beide eine Fortsetzung planen, aber sobald der Kontrollfluss noch etwas komplexer wird,
await
führt dies zu viel einfacherem Code. Wie von Servy in den Kommentaren erwähnt, werden beim Warten auf eine Aufgabe außerdem aggregierte Ausnahmen "entpackt", was normalerweise zu einer einfacheren Fehlerbehandlung führt. Mit usingawait
wird implizit die Fortsetzung im aufrufenden Kontext geplant (sofern Sie nicht verwendenConfigureAwait
). Es ist nichts, was nicht "manuell" gemacht werden kann, aber es ist viel einfacher, es damit zu machenawait
.Ich schlage vor, Sie versuchen, mit beiden eine etwas größere Abfolge von Operationen zu implementieren,
await
undTask.ContinueWith
- es kann ein echter Augenöffner sein.quelle
await
Over zu arbeitenContinueWith
.parseData
ausgeführt wird.Hier ist die Folge von Codefragmenten, die ich kürzlich verwendet habe, um den Unterschied und verschiedene Probleme bei der Verwendung asynchroner Lösungen zu veranschaulichen.
Angenommen, Sie haben einen Ereignishandler in Ihrer GUI-basierten Anwendung, der viel Zeit in Anspruch nimmt, und möchten ihn daher asynchron machen. Hier ist die synchrone Logik, mit der Sie beginnen:
LoadNextItem gibt eine Aufgabe zurück, die schließlich zu einem Ergebnis führt, das Sie überprüfen möchten. Wenn das aktuelle Ergebnis das gesuchte ist, aktualisieren Sie den Wert eines Zählers auf der Benutzeroberfläche und kehren von der Methode zurück. Andernfalls verarbeiten Sie weitere Elemente aus LoadNextItem.
Erste Idee für die asynchrone Version: Verwenden Sie einfach Fortsetzungen! Und lassen Sie uns den Loop-Teil vorerst ignorieren. Ich meine, was könnte möglicherweise schief gehen?
Großartig, jetzt haben wir eine Methode, die nicht blockiert! Es stürzt stattdessen ab. Alle Aktualisierungen der UI-Steuerelemente sollten im UI-Thread erfolgen, daher müssen Sie dies berücksichtigen. Zum Glück gibt es eine Option, mit der festgelegt werden kann, wie Fortsetzungen geplant werden sollen, und es gibt eine Standardoption für genau dies:
Großartig, jetzt haben wir eine Methode, die nicht abstürzt! Es schlägt stattdessen lautlos fehl. Fortsetzungen sind separate Aufgaben selbst, deren Status nicht an den der vorherigen Aufgabe gebunden ist. Selbst wenn LoadNextItem fehlerhaft ist, wird dem Aufrufer nur eine Aufgabe angezeigt, die erfolgreich abgeschlossen wurde. Okay, dann geben Sie einfach die Ausnahme weiter, falls es eine gibt:
Großartig, jetzt funktioniert das tatsächlich. Für einen einzelnen Artikel. Wie wäre es nun mit dieser Schleife? Es stellt sich heraus, dass eine Lösung, die der Logik der ursprünglichen synchronen Version entspricht, ungefähr so aussieht:
Anstelle der oben genannten Schritte können Sie auch Async verwenden, um dasselbe zu tun:
Das ist jetzt viel schöner, nicht wahr?
quelle