Ich versuche, Async Wait in der einfachsten Form zu verstehen. Ich möchte eine sehr einfache Methode erstellen, die für dieses Beispiel zwei Zahlen hinzufügt. Zugegeben, es ist überhaupt keine Verarbeitungszeit, es geht nur darum, hier ein Beispiel zu formulieren.
Beispiel 1
private async Task DoWork1Async()
{
int result = 1 + 2;
}
Beispiel 2
private async Task DoWork2Async()
{
Task.Run( () =>
{
int result = 1 + 2;
});
}
Wenn ich warte, DoWork1Async()
wird der Code synchron oder asynchron ausgeführt?
Muss ich den Synchronisierungscode Task.Run
umbrechen, damit die Methode abwartet UND asynchron ist, um den UI-Thread nicht zu blockieren?
Ich versuche herauszufinden, ob meine Methode eine ist Task
oder zurückgibt. Task<T>
Muss ich den Code umschließen Task.Run
, um ihn asynchron zu machen?
Dumme Frage Ich bin mir sicher, aber ich sehe Beispiele im Internet, in denen Leute auf Code warten, der nichts Asynchrones enthält und nicht in ein Task.Run
oder eingeschlossen ist StartNew
.
quelle
Antworten:
Lassen Sie uns zunächst einige Begriffe klären: "asynchron" (
async
) bedeutet, dass der aufrufende Thread möglicherweise vor dem Start wieder kontrolliert wird. In einerasync
Methode sind diese "Fließpunkte"await
Ausdrücke.Dies unterscheidet sich stark von dem Begriff "asynchron", der in der MSDN-Dokumentation seit Jahren (falsch) als "auf einem Hintergrundthread ausgeführt" verwendet wird.
Das Thema weiter zu verwirren,
async
ist ganz anders als "erwartbar"; Es gibt einigeasync
Methoden, deren Rückgabetypen nicht erwartet werden können, und viele Methoden, die erwartete Typen zurückgeben, die nicht erwartet werdenasync
.Genug davon, was sie nicht sind ; Hier ist was sie sind :
async
Schlüsselwort erlaubt eine asynchrone Methode (dh es erlaubtawait
Ausdrücke).async
Methoden können zurückgebenTask
,Task<T>
oder (wenn Sie müssen)void
.Task
undTask<T>
.Wenn wir Ihre Frage so umformulieren, dass "Wie kann ich eine Operation für einen Hintergrundthread so ausführen, dass sie erwartet wird", lautet die Antwort
Task.Run
:(Aber dieses Muster ist ein schlechter Ansatz; siehe unten).
Wenn Ihre Frage jedoch lautet: "Wie erstelle ich eine
async
Methode, die dem Aufrufer zurückgeben kann, anstatt sie zu blockieren?", Lautet die Antwort, die Methode zu deklarierenasync
undawait
für ihre "Nachgiebigkeitspunkte" zu verwenden:Das Grundmuster der Dinge besteht also darin, dass
async
Code in seinenawait
Ausdrücken von "Wartbaren" abhängt . Diese "Wartezeiten" können andereasync
Methoden oder nur reguläre Methoden sein, die Wartezeiten zurückgeben. Reguläre Methoden zurückzukehrTask
/Task<T>
können verwendenTask.Run
Code auf einem Hintergrund - Thread auszuführen, oder (häufiger) kann sie verwenden ,TaskCompletionSource<T>
oder eines ihrer Verknüpfungen (TaskFactory.FromAsync
,Task.FromResult
usw.). Ich empfehle nicht , eine ganze MethodeTask.Run
einzuschließen. Synchrone Methoden sollten synchrone Signaturen haben, und es sollte dem Verbraucher überlassen bleiben, ob sie in Folgendes verpackt werden sollenTask.Run
:Ich habe ein
async
/await
Intro in meinem Blog. Am Ende stehen einige gute Follow-up-Ressourcen. Auch die MSDN-Dokumente fürasync
sind ungewöhnlich gut.quelle
async
Methoden müssen zurückkehrenTask
,Task<T>
odervoid
.Task
undTask<T>
sind zu erwarten;void
ist nicht.async void
Methodensignatur kompiliert, es ist nur eine ziemlich schreckliche Idee, wenn Sie Ihren Zeiger auf Ihre asynchrone Aufgabevoid
nicht erwartet.Task.Run
(wieDoWorkAsync
in dieser Antwort). Die VerwendungTask.Run
zum Aufrufen einer Methode aus einem UI-Kontext ist angemessen (wieDoVariousThingsFromTheUIThreadAsync
).Task.Run
Task.Run
Task.Run
Eines der wichtigsten Dinge, die Sie beim Dekorieren einer Methode mit Async beachten sollten, ist, dass sich innerhalb der Methode mindestens ein wartender Operator befindet. In Ihrem Beispiel würde ich es wie unten gezeigt mit TaskCompletionSource übersetzen .
quelle
Wenn Sie Task.Run zum Ausführen einer Methode verwenden, ruft Task einen Thread aus dem Threadpool ab, um diese Methode auszuführen. Aus Sicht des UI-Threads ist es also "asynchron", da es den UI-Thread nicht blockiert. Dies ist für Desktop-Anwendungen in Ordnung, da Sie normalerweise nicht viele Threads benötigen, um sich um Benutzerinteraktionen zu kümmern.
Für Webanwendungen wird jedoch jede Anforderung von einem Thread-Pool-Thread bearbeitet, und daher kann die Anzahl der aktiven Anforderungen durch Speichern solcher Threads erhöht werden. Die häufige Verwendung von Threadpool-Threads zur Simulation eines asynchronen Vorgangs ist für Webanwendungen nicht skalierbar.
Bei True Async muss nicht unbedingt ein Thread für E / A-Vorgänge wie Datei- / DB-Zugriff usw. verwendet werden. Sie können dies lesen, um zu verstehen, warum für E / A-Vorgänge keine Threads erforderlich sind. http://blog.stephencleary.com/2013/11/there-is-no-thread.html
In Ihrem einfachen Beispiel handelt es sich um eine reine CPU-gebundene Berechnung, daher ist die Verwendung von Task.Run in Ordnung.
quelle
I should NOT wrap the synchronous call in Task.Run()
das ist richtig. Wenn Sie dies tun, würden Sie nur die Threads wechseln. Das heißt, Sie entsperren den anfänglichen Anforderungsthread, nehmen jedoch einen anderen Thread aus dem Threadpool, der zur Verarbeitung einer anderen Anforderung hätte verwendet werden können. Das einzige Ergebnis ist ein Kontextwechsel-Overhead, wenn der Anruf für eine Verstärkung von absolut Null abgeschlossen ist