Zunächst klären wir einige Begriffe: "asynchron" (async
) bedeutet, dass es möglicherweise die Kontrolle an den aufrufenden Thread zurückgibt, bevor es startet. In einer async
Methode sind diese "yield" Punkte await
Ausdrücke.
Dies unterscheidet sich sehr von dem Begriff "asynchron", wie er von der MSDN-Dokumentation jahrelang (falsch) verwendet wurde, um "auf einem Hintergrundthread ausgeführt" zu bedeuten.
Um die Verwirrung noch weiter zu steigern, ist async
sehr unterschiedlich von "awaitable"; es gibt einige async
Methoden, deren Rückgabetypen nicht awaitable sind, und viele Methoden, die awaitable Typen zurückgeben, die nicht async
sind.
Genug davon, was sie nicht sind; hier ist, was sie sind:
- Das
async
Schlüsselwort ermöglicht eine asynchrone Methode (es erlaubt await
Ausdrücke). async
Methoden können Task
, Task
oder (wenn nötig) void
zurückgeben.
- Jeder Typ, der einem bestimmten Muster folgt, kann awaitable sein. Die häufigsten awaitable Typen sind
Task
und Task
.
Also, wenn wir Ihre Frage umformulieren zu "wie kann ich eine Operation auf einem Hintergrundthread ausführen, so dass sie awaitable ist", lautet die Antwort, Task.Run
zu verwenden:
private Task DoWorkAsync() // Kein async, da die Methode nicht auf await angewiesen ist
{
return Task.Run(() =>
{
return 1 + 2;
});
}
(Aber dieses Muster ist ein schlechter Ansatz; siehe unten).
Aber wenn Ihre Frage lautet "wie erstelle ich eine async
Methode, die an den Aufrufer zurückgeben kann anstatt zu blockieren", lautet die Antwort, die Methode als async
zu deklarieren und await
für ihre "yielding" Punkte zu verwenden:
private async Task GetWebPageHtmlSizeAsync()
{
var client = new HttpClient();
var html = await client.GetAsync("http://www.example.com/");
return html.Length;
}
Also, das grundlegende Muster der Dinge ist, dass async
Code von "awaitables" in seinen await
Ausdrücken abhängt. Diese "awaitables" können andere async
Methoden oder einfach reguläre Methoden, die awaitables zurückgeben, sein. Reguläre Methoden, die Task
/Task
zurückgeben, können Task.Run
verwenden, um Code auf einem Hintergrundthread auszuführen, oder sie können häufiger TaskCompletionSource
oder eine seiner Abkürzungen (z.B. TaskFactory.FromAsync
, Task.FromResult
, etc) verwenden. Ich empfehle nicht, eine gesamte Methode in Task.Run
zu verpacken; synchrone Methoden sollten synchrone Signaturen haben und es sollte dem Verbraucher überlassen werden, ob sie in ein Task.Run
eingehüllt werden sollten:
private int DoWork()
{
return 1 + 2;
}
private void MoreSynchronousProcessing()
{
// Führen Sie es direkt (synchron) aus, da wir auch eine synchrone Methode sind.
var result = DoWork();
...
}
private async Task DoVariousThingsFromTheUIThreadAsync()
{
// Ich habe eine Menge async-Arbeiten zu erledigen und werde auf dem UI-Thread ausgeführt.
var result = await Task.Run(() => DoWork());
...
}
Ich habe eine async
/await
Einführung auf meinem Blog; am Ende gibt es einige gute Nachschlagewerke. Die MSDN-Dokumentation zu async
ist ebenfalls ungewöhnlich gut.