webentwicklung-frage-antwort-db.com.de

Müssen Sie Task.Run in eine Methode einfügen, um sie asynchron zu machen?

Ich versuche zu verstehen, wie Async in der einfachsten Form wartet. Ich möchte für dieses Beispiel eine sehr einfache Methode erstellen, die zwei Zahlen hinzufügt, vorausgesetzt, 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 auf DoWork1Async() warte, wird der Code synchron oder asynchron ausgeführt?

Muss ich den Synchronisierungscode mit Task.Run umbrechen, um die Methode wartbar UND asynchron zu machen, damit der UI-Thread nicht blockiert wird?

Ich versuche herauszufinden, ob meine Methode Task ist oder Task<T> zurückgibt. Muss ich den Code mit Task.Run umbrechen, um ihn asynchron zu machen?.

Dumme Frage, da bin ich mir sicher, aber ich sehe Beispiele im Netz, in denen Leute auf Code warten, der nichts Asynchrones enthält und nicht in einen Task.Run oder StartNew eingeschlossen ist.

283
Neal

Lassen Sie uns zunächst einige Begriffe klären: "Asynchron" (async) bedeutet, dass der aufrufende Thread möglicherweise die Kontrolle erhält, bevor er gestartet wird. In einer async Methode sind diese "Ertragspunkte" await Ausdrücke.

Dies unterscheidet sich stark von dem Begriff "asynchron", wie er in der MSDN-Dokumentation jahrelang als "auf einem Hintergrund-Thread ausgeführt" (falsch) verwendet wird.

Um das Problem weiter zu verwirren, ist async ganz anders als "erwartbar"; Es gibt einige async Methoden, deren Rückgabetypen nicht erwartet werden können, und viele Methoden, die erwartete Typen zurückgeben, die nicht async sind.

Genug davon, was sie nicht sind ; Das sind sie :

  • Das Schlüsselwort async ermöglicht eine asynchrone Methode (dh, es ermöglicht await Ausdrücke). async Methoden können Task, Task<T> oder (falls erforderlich) void zurückgeben.
  • Jeder Typ, der einem bestimmten Muster folgt, kann erwartet werden. Die am häufigsten zu erwartenden Typen sind Task und Task<T>.

Wenn wir also Ihre Frage in "Wie kann ich eine Operation auf einem Hintergrund-Thread so ausführen, dass es absehbar ist" umformulieren, lautet die Antwort Task.Run:

private Task<int> DoWorkAsync() // No async because the method does not need await
{
  return Task.Run(() =>
  {
    return 1 + 2;
  });
}

(Aber dieses Muster ist ein schlechter Ansatz; siehe unten).

Lautet Ihre Frage jedoch "Wie erstelle ich eine async -Methode, die dem Aufrufer zurückgegeben werden kann, anstatt ihn zu blockieren", deklarieren Sie die async -Methode und verwenden Sie await "Nachgebende" Punkte:

private async Task<int> GetWebPageHtmlSizeAsync()
{
  var client = new HttpClient();
  var html = await client.GetAsync("http://www.example.com/");
  return html.Length;
}

Das Grundmuster der Dinge ist also, dass async Code von "awaitables" in seinen await Ausdrücken abhängt. Diese "erwarteten Ergebnisse" können andere async Methoden oder nur reguläre Methoden sein, die erwartete Ergebnisse zurückgeben. Reguläre Methoden, die Task/Task<T> zurückgeben, können Task.Run verwenden, um Code in einem Hintergrund-Thread auszuführen, oder (häufiger) TaskCompletionSource<T> oder eine seiner Verknüpfungen (TaskFactory.FromAsync, Task.FromResult, etc). Ich empfehle nicht , eine ganze Methode in Task.Run zu schreiben; synchrone Methoden sollten synchrone Signaturen haben und es sollte dem Konsumenten überlassen bleiben, ob sie in einen Task.Run eingeschlossen werden sollen:

private int DoWork()
{
  return 1 + 2;
}

private void MoreSynchronousProcessing()
{
  // Execute it directly (synchronously), since we are also a synchronous method.
  var result = DoWork();
  ...
}

private async Task DoVariousThingsFromTheUIThreadAsync()
{
  // I have a bunch of async work to do, and I am executed on the UI thread.
  var result = await Task.Run(() => DoWork());
  ...
}

Ich habe ein async/await Intro in meinem Blog; Am Ende stehen einige gute Nachverfolgungsressourcen. Die MSDN-Dokumente für async sind auch ungewöhnlich gut.

545
Stephen Cleary

Eines der wichtigsten Dinge, an die Sie denken müssen, wenn Sie eine Methode mit async dekorieren, ist, dass es zumindest eine gibt eins warten Operator innerhalb der Methode. In Ihrem Beispiel würde ich es wie unten gezeigt mit TaskCompletionSource übersetzen.

private Task<int> DoWorkAsync()
{
    //create a task completion source
    //the type of the result value must be the same
    //as the type in the returning Task
    TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
    Task.Run(() =>
    {
        int result = 1 + 2;
        //set the result to TaskCompletionSource
        tcs.SetResult(result);
    });
    //return the Task
    return tcs.Task;
}

private async void DoWork()
{
    int result = await DoWorkAsync();
}
22
Ronald Ramos

Wenn Sie eine Methode mit Task.Run ausführen, ruft Task einen Thread aus dem Threadpool ab, um diese Methode auszuführen. Aus der Sicht des UI-Threads ist es "asynchron", da es den UI-Thread nicht blockiert. Dies ist in Ordnung für Desktop-Anwendungen, da Sie normalerweise nicht viele Threads benötigen, um Benutzerinteraktionen zu erledigen.

Für Webanwendungen wird jede Anforderung jedoch 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.

True Async erfordert nicht unbedingt die Verwendung eines Threads für E/A-Vorgänge wie Datei-/Datenbankzugriff usw. 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.

12
zheng yu