373 Stimmen

Müssen Sie Task.Run in einer Methode platzieren, um sie asynchron zu machen?

Ich versuche, async await in seiner einfachsten Form zu verstehen. Ich möchte eine sehr einfache Methode erstellen, die zwei Zahlen addiert, nur für dieses Beispiel, zugegebenermaßen, es gibt ü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 DoWork1Async() awaite, wird der Code synchron oder asynchron ausgeführt?

Muss ich den synchronen Code mit Task.Run umschließen, um die Methode awaitable UND asynchron zu machen, damit der UI-Thread nicht blockiert wird?

Ich versuche herauszufinden, ob meine Methode ein Task ist oder Task zurückgibt, ob ich den Code mit Task.Run umschließen muss, um ihn asynchron zu machen.

Ich sehe Beispiele im Netz, wo Leute Code awaiten, der nichts Async-artiges hat und nicht mit Task.Run oder StartNew umschlossen ist.

698voto

Stephen Cleary Punkte 402664

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.

30voto

Ronald Ramos Punkte 5200

Eines der wichtigsten Dinge, die man sich merken sollte, wenn man eine Methode mit async dekoriert, ist, dass mindestens ein await-Operator innerhalb der Methode vorhanden sein muss. In Ihrem Beispiel würde ich es wie unten gezeigt mithilfe von TaskCompletionSource übersetzen.

private Task DoWorkAsync()
{
    //eine Task-Completion-Source erstellen
    //Der Typ des Ergebniswerts muss gleich sein
    //wie der Typ in der zurückgegebenen Task
    TaskCompletionSource tcs = new TaskCompletionSource();
    Task.Run(() =>
    {
        int result = 1 + 2;
        //das Ergebnis auf TaskCompletionSource setzen
        tcs.SetResult(result);
    });
    //die Task zurückgeben
    return tcs.Task;
}

private async Task DoWork()
{
    int result = await DoWorkAsync();
}

19voto

zheng yu Punkte 297

Wenn Sie Task.Run verwenden, um eine Methode auszuführen, erhält Task einen Thread aus dem Threadpool, um diese Methode auszuführen. Aus der Perspektive des UI-Threads ist es daher "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 Benutzerinteraktionen zu verwalten.

Für Webanwendungen wird jedoch jeder Request von einem Thread-Pool-Thread bedient und somit kann die Anzahl der aktiven Requests durch Speicherung solcher Threads erhöht werden. Die häufige Verwendung von Threadpool-Threads zur Simulation von asynchronen Operationen ist nicht skalierbar für Webanwendungen.

True Async beinhaltet nicht unbedingt die Verwendung eines Threads für I/O-Operationen, wie z.B. Datei-/DB-Zugriff usw. Sie können dies lesen, um zu verstehen, warum I/O-Operationen keine Threads benötigen. 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.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X