Die Antworten von SLaks und Killercam sind gut; Ich dachte, ich würde nur ein bisschen mehr Kontext hinzufügen.
Ihre erste Frage betrifft im Wesentlichen, welche Methoden markiert werden können async
.
Eine Methode, die als zurückgegeben werden async
kann void
, Task
oder Task<T>
. Was sind die Unterschiede zwischen ihnen?
Eine Task<T>
zurückkehrende asynchrone Methode kann erwartet werden, und wenn die Aufgabe abgeschlossen ist, wird ein T angeboten.
Eine Task
zurückkehrende asynchrone Methode kann abgewartet werden. Wenn die Aufgabe abgeschlossen ist, soll die Fortsetzung der Aufgabe ausgeführt werden.
Eine void
zurückkehrende asynchrone Methode kann nicht erwartet werden. Es ist eine "Feuer und Vergessen" -Methode. Es funktioniert asynchron und Sie können nicht sagen, wann es fertig ist. Das ist mehr als ein bisschen komisch; Wie SLaks sagt, würden Sie dies normalerweise nur tun, wenn Sie einen asynchronen Ereignishandler erstellen. Das Ereignis wird ausgelöst, der Handler wird ausgeführt. Niemand wird auf die vom Ereignishandler zurückgegebene Aufgabe "warten", da Ereignishandler keine Aufgaben zurückgeben, und selbst wenn dies der Fall wäre, welcher Code würde die Aufgabe für etwas verwenden? Normalerweise überträgt der Benutzercode die Kontrolle überhaupt nicht an den Handler.
Ihre zweite Frage in einem Kommentar betrifft im Wesentlichen das, was bearbeitet werden kann await
:
Welche Arten von Methoden können await
bearbeitet werden? Kann eine Methode zur Rückgabe von Leeren await
bearbeitet werden?
Nein, eine Rückgabemethode kann nicht erwartet werden. Der Compiler übersetzt await M()
in einen Aufruf von M().GetAwaiter()
, wobei es sich GetAwaiter
möglicherweise um eine Instanzmethode oder eine Erweiterungsmethode handelt. Der erwartete Wert muss einer sein, für den Sie einen Kellner erhalten können. Es ist klar, dass eine Methode zur Rückgabe von Leeren keinen Wert erzeugt, von dem Sie einen Kellner erhalten können.
Task
-Rückgabemethoden können erwartete Werte erzeugen. Wir gehen davon aus, dass Dritte ihre eigenen Implementierungen von Task
ähnlichen Objekten erstellen möchten, auf die gewartet werden kann, und dass Sie darauf warten können. Allerdings werden Sie nicht erlaubt werden , zu erklären , async
alles Methoden , die Rückkehr aber void
, Task
oder Task<T>
.
(UPDATE: Mein letzter Satz dort wird möglicherweise durch eine zukünftige Version von C # verfälscht. Es gibt einen Vorschlag, andere Rückgabetypen als Aufgabentypen für asynchrone Methoden zuzulassen.)
(UPDATE: Die oben erwähnte Funktion hat es in C # 7 geschafft.)
async void
Methoden lösen ihre Ausnahme für die ausSynchronizationContext
, die zum Zeitpunkt der Ausführung aktiv war. Dies ähnelt dem Verhalten von (synchronen) Ereignishandlern. @DrewMarsh: DieUnobservedTaskException
Einstellung und zur Laufzeit gilt nur für asynchrone Task- Methoden "Feuer und Vergessen" , nicht fürasync void
Methoden.Falls der Anrufer auf die Aufgabe warten oder eine Fortsetzung hinzufügen möchte.
Der einzige Grund für die Rückkehr
void
ist, wenn Sie nicht zurückkehren können,Task
weil Sie einen Ereignishandler schreiben.quelle
void
, haben Sie keine Möglichkeit, an die von ihr generierte Aufgabe zu gelangen. (Eigentlich bin ich mir nicht sicher, ob es überhaupt eine erzeugtTask
)Methoden, die zurückkehren
Task
und zusammensetzbarTask<T>
sind - das heißt, Sie könnenawait
sie innerhalb einerasync
Methode verwenden.async
Rückgabemethodenvoid
sind nicht zusammensetzbar, haben jedoch zwei weitere wichtige Eigenschaften:Der zweite Punkt ist wichtig, wenn Sie sich mit einem Kontext befassen, in dem die Anzahl der ausstehenden asynchronen Operationen beibehalten wird .
Der ASP.NET-Kontext ist ein solcher Kontext. Wenn Sie asynchrone
Task
Methoden verwenden, ohne sie von einer asynchronenvoid
Methode abzuwarten , wird die ASP.NET-Anforderung zu früh abgeschlossen.Ein weiterer Kontext ist der, den
AsyncContext
ich für Unit-Tests geschrieben habe ( hier verfügbar ). DieAsyncContext.Run
Methode verfolgt die Anzahl der ausstehenden Vorgänge und kehrt zurück, wenn sie Null ist.quelle
Typ
Task<T>
ist der Arbeitspferdetyp der Task Parallel Library (TPL). Er repräsentiert das Konzept von "einigen Arbeiten / Jobs,T
die in Zukunft ein typisches Ergebnis liefern werden". Das Konzept "Arbeit, die in Zukunft abgeschlossen sein wird, aber kein Ergebnis liefert" wird durch den nicht generischen Aufgabentyp dargestellt.Genau wie das Ergebnis des Typs
T
erzeugt wird, ist und Implementierungsdetail einer bestimmten Aufgabe; Die Arbeit wird möglicherweise an einen anderen Prozess auf dem lokalen Computer, an einen anderen Thread usw. ausgelagert. TPL-Aufgaben werden im aktuellen Prozess normalerweise an Arbeitsthreads aus einem Thread-Pool ausgelagert, aber dieses Implementierungsdetail ist für denTask<T>
Typ nicht grundlegend . VielmehrTask<T>
kann a jede Operation mit hoher Latenz darstellen, die a erzeugtT
.Basierend auf Ihrem Kommentar oben:
Der
await
Ausdruck bedeutet "diesen Ausdruck auswerten, um ein Objekt zu erhalten, das eine Arbeit darstellt, die in Zukunft ein Ergebnis hervorbringen wird. Melden Sie den Rest der aktuellen Methode als Rückruf an, der mit der Fortsetzung dieser Aufgabe verbunden ist. Sobald diese Aufgabe erstellt wurde und der Rückruf erfolgt." ist angemeldet, sofort die Kontrolle an meinen Anrufer zurückgeben ". Dies steht im Gegensatz zu einem regulären Methodenaufruf, der bedeutet: "Denken Sie daran, was Sie tun, führen Sie diese Methode aus, bis sie vollständig abgeschlossen ist, und setzen Sie sie dort fort, wo Sie aufgehört haben, und kennen Sie nun das Ergebnis der Methode."Bearbeiten: Ich sollte Eric Lipperts Artikel im Oktober 2011 im MSDN Magazine zitieren, da dies eine große Hilfe für mich war, um dieses Zeug überhaupt zu verstehen.
Weitere Informationen und Whitepages finden Sie hier .
Ich hoffe, das ist hilfreich.
quelle