Wie und wann 'async' und 'await' verwendet werden sollen


1065

Von meinem Verständnis eines der wichtigsten Dinge , die asyncundawait tun , ist Code einfach zu schreiben zu machen und lesen - aber mit ihnen zu Laichen Hintergrund - Threads gleich Logik langen Dauer durchzuführen?

Ich probiere gerade das grundlegendste Beispiel aus. Ich habe einige Kommentare inline hinzugefügt. Kannst du es mir erklären?

// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
    Task<int> access = DoSomethingAsync();
    // task independent stuff here

    // this line is reached after the 5 seconds sleep from 
    // DoSomethingAsync() method. Shouldn't it be reached immediately? 
    int a = 1; 

    // from my understanding the waiting should be done here.
    int x = await access; 
}

async Task<int> DoSomethingAsync()
{
    // is this executed on a background thread?
    System.Threading.Thread.Sleep(5000);
    return 1;
}

48
Beachten Sie in Ihrem Beispiel auch, dass Sie beim Kompilieren des obigen Codes eine Warnung erhalten. Beachten Sie die Warnung . Es sagt Ihnen, dass dieser Code keinen Sinn ergibt.
Eric Lippert

Antworten:


759

Bei Verwendung asyncund awaitgeneriert der Compiler eine Zustandsmaschine im Hintergrund.

Hier ist ein Beispiel, an dem ich hoffentlich einige der wichtigsten Details erläutern kann:

public async Task MyMethodAsync()
{
    Task<int> longRunningTask = LongRunningOperationAsync();
    // independent work which doesn't need the result of LongRunningOperationAsync can be done here

    //and now we call await on the task 
    int result = await longRunningTask;
    //use the result 
    Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
{
    await Task.Delay(1000); // 1 second delay
    return 1;
}

OK, was passiert hier?

  1. Task<int> longRunningTask = LongRunningOperationAsync(); beginnt mit der Ausführung LongRunningOperation

  2. Nehmen wir an, dass der Haupt-Thread (Thread-ID = 1) await longRunningTaskerreicht ist.

    Wenn der longRunningTasknicht fertig ist und noch ausgeführt MyMethodAsync()wird, kehrt er zu seiner aufrufenden Methode zurück, sodass der Hauptthread nicht blockiert wird. Wenn longRunningTaskdies erledigt ist, kehrt ein Thread aus dem ThreadPool (kann ein beliebiger Thread sein) MyMethodAsync()in seinen vorherigen Kontext zurück und setzt die Ausführung fort (in diesem Fall wird das Ergebnis auf der Konsole gedruckt).

Ein zweiter Fall wäre, dass der longRunningTaskdie Ausführung bereits beendet hat und das Ergebnis verfügbar ist. Wenn await longRunningTaskwir das erreichen, haben wir bereits das Ergebnis, sodass der Code weiterhin auf demselben Thread ausgeführt wird. (In diesem Fall wird das Ergebnis auf die Konsole gedruckt.) Dies ist natürlich nicht der Fall für das obige Beispiel, bei dem es sich um einen handelt Task.Delay(1000).


65
Warum haben wir ein "Warten" mit der "Task.Delay (1000)"; in der asynchronen LongRunningOperation-Methode?
Benison Sam

3
@codea In Eric Lipperts Kommentaren zu dem Artikel hat er einen Einführungsartikel zu diesem Thema verlinkt, in dem er die DoEvents-Strategie speziell mit async-await vergleicht
Camilo Martinez

13
@BenisonSam Thread ist ein bisschen alt, aber ich hatte die gleiche Frage und habe nach einer Antwort gesucht. Der Grund für das "Warten" ist, dass LongRunningOperationAsync () sofort zurückkehrt, wenn wir das "Warten" weglassen. Tatsächlich gibt der Compiler eine Warnung aus, wenn wir das Warten entfernen. Stephen Clearys Blogpost blog.stephencleary.com/2011/09/… gibt einen Überblick über die Designdiskussionen.
Shelbypereira

70
Wenn jede asynchrone Methode ein Warten enthalten muss und ein Warten nur für asynchrone Methoden möglich ist, wann wird sie beendet?
Bruno Santos

108
Diese Antwort ist eindeutig falsch. Diese vielen Upvotes führen bei vielen Benutzern zu falschem Verständnis. In der MS-Dokumentation heißt es eindeutig, dass kein anderer Thread verwendet wird, wenn nur Async verwendet wird. msdn.microsoft.com/en-us/library/mt674882.aspx Bitte korrigieren Sie die Antwort. Aus diesem Grund habe ich einen ganzen Tag verschwendet.
Krishna Deepak

171

Nach meinem Verständnis ist eines der wichtigsten Dinge, die asynchron und wartend sind, das einfache Schreiben und Lesen von Code.

Sie sollen asynchronen Code einfach zu schreiben und zu lesen machen, ja.

Ist es dasselbe wie das Laichen von Hintergrund-Threads, um eine Logik mit langer Dauer auszuführen?

Ganz und gar nicht.

// Ich verstehe nicht, warum diese Methode als 'async' markiert werden muss.

Das asyncSchlüsselwort aktiviert das awaitSchlüsselwort. Daher muss jede verwendete Methode awaitmarkiert werden async.

// Diese Zeile wird nach 5 Sekunden Ruhezeit von der DoSomethingAsync () -Methode erreicht. Sollte es nicht sofort erreicht werden?

Nein, da asyncMethoden standardmäßig nicht auf einem anderen Thread ausgeführt werden.

// Wird dies in einem Hintergrundthread ausgeführt?

Nein.


Sie können mein async/ awaitIntro hilfreich finden. Die offiziellen MSDN-Dokumente sind ebenfalls ungewöhnlich gut (insbesondere der TAP- Bereich), und das asyncTeam hat eine hervorragende FAQ veröffentlicht .


6
Es läuft also nicht in einem Hintergrund-Thread, sondern blockiert auch nicht. Dies ist aufgrund asynchroner APIs möglich, die Rückrufe verwenden, anstatt mit Threads zu jonglieren. Sie initiieren eine Operation (E / A, Socket, ..) und kehren zu Ihren Aufgaben zurück. Wenn der Vorgang abgeschlossen ist, ruft das Betriebssystem den Rückruf auf. Dies ist, was Node.js oder das Python Twisted-Framework tun, und sie haben auch einige nette Erklärungen.
Roman Plášil

3
"Das Schlüsselwort async aktiviert das Schlüsselwort await. Daher muss jede Methode, die await verwendet, als asynchron markiert werden.", - aber warum? Diese Antwort hilft nicht zu verstehen, warum die Methode als asynchron markiert werden muss. Kann der Compiler nicht einfach darauf schließen, dass die Methode asynchron ist, indem er nach wartenden Schlüsselwörtern sucht?
Stanislav

9
@ Stanislav: Ich habe einen Blogeintrag , der diese Frage behandelt.
Stephen Cleary

3
Vorgeschlagene Klarstellung: Nein, da asyncMethoden standardmäßig nicht auf einem anderen Thread ausgeführt werden. In Ihrem Beispiel blockiert der Sleep()Aufruf innerhalb DoSomethingAsync()den aktuellen Thread, wodurch verhindert wird, dass die Ausführung innerhalb button1_Click()bis zum DoSomethingAsync()Abschluss fortgesetzt wird . Beachten Sie, dass, während Thread.Sleep()der ausführende Thread blockiert,Task.Delay() does not.
DavidRR

166

Erläuterung

Hier ist ein kurzes Beispiel für async/ awaitauf hohem Niveau. Darüber hinaus sind noch viele weitere Details zu berücksichtigen.

Hinweis: Task.Delay(1000)Simuliert die Arbeit für 1 Sekunde. Ich denke, es ist am besten, sich das als Warten auf eine Antwort von einer externen Ressource vorzustellen. Da unser Code auf eine Antwort wartet, kann das System die laufende Aufgabe zur Seite stellen und nach Abschluss darauf zurückgreifen. In der Zwischenzeit kann es andere Arbeiten an diesem Thread ausführen.

Im folgenden Beispiel macht der erste Block genau das. Es startet alle Aufgaben sofort (die Task.DelayLinien) und setzt sie zur Seite. Der Code wird in der await aZeile angehalten , bis die Verzögerung von 1 Sekunde erreicht ist, bevor zur nächsten Zeile gewechselt wird. Da b, c, d, und ealle Ausführung fast zur gleichen Zeit wie der Autor a(wegen des Mangels an der await), sollten sie in etwa zur gleichen Zeit in diesem Fall beenden.

Im folgenden Beispiel startet der zweite Block eine Aufgabe und wartet darauf, dass sie beendet wird (genau das awaittut er), bevor die nachfolgenden Aufgaben gestartet werden. Jede Iteration dauert 1 Sekunde. Das awaitProgramm wird angehalten und auf das Ergebnis gewartet, bevor fortgefahren wird. Dies ist der Hauptunterschied zwischen dem ersten und dem zweiten Block.

Beispiel

Console.WriteLine(DateTime.Now);

// This block takes 1 second to run because all
// 5 tasks are running simultaneously
{
    var a = Task.Delay(1000);
    var b = Task.Delay(1000);
    var c = Task.Delay(1000);
    var d = Task.Delay(1000);
    var e = Task.Delay(1000);

    await a;
    await b;
    await c;
    await d;
    await e;
}

Console.WriteLine(DateTime.Now);

// This block takes 5 seconds to run because each "await"
// pauses the code until the task finishes
{
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
}
Console.WriteLine(DateTime.Now);

AUSGABE:

5/24/2017 2:22:50 PM
5/24/2017 2:22:51 PM (First block took 1 second)
5/24/2017 2:22:56 PM (Second block took 5 seconds)

Zusätzliche Informationen zu SynchronizationContext

Hinweis: Hier wird es für mich etwas neblig. Wenn ich also in irgendetwas falsch liege, korrigieren Sie mich bitte und ich werde die Antwort aktualisieren. Es ist wichtig, ein grundlegendes Verständnis dafür zu haben, wie dies funktioniert, aber Sie können auskommen, ohne ein Experte dafür zu sein, solange Sie es nie verwenden ConfigureAwait(false), obwohl Sie wahrscheinlich eine Möglichkeit zur Optimierung verpassen werden, nehme ich an.

Es ist ein Aspekt dieser , die das macht async/ awaitKonzept etwas schwieriger zu erreichen. Das ist die Tatsache, dass in diesem Beispiel alles auf demselben Thread geschieht (oder zumindest, was in Bezug auf seinen Thread derselbe zu sein scheint SynchronizationContext). Standardmäßig awaitwird der Synchronisierungskontext des ursprünglichen Threads wiederhergestellt, auf dem er ausgeführt wurde. In ASP.NET haben Sie beispielsweise einen, HttpContextder an einen Thread gebunden ist, wenn eine Anforderung eingeht. Dieser Kontext enthält Dinge, die für die ursprüngliche HTTP-Anforderung spezifisch sind, z. B. das ursprüngliche Anforderungsobjekt, das beispielsweise Sprache, IP-Adresse, Header usw. Enthält Wenn Sie den Thread nach der Hälfte der Verarbeitung wechseln, versuchen Sie möglicherweise, Informationen aus diesem Objekt auf einem anderen Objekt abzurufenHttpContextdas könnte katastrophal sein. Wenn Sie wissen, dass Sie den Kontext für nichts verwenden, können Sie sich dafür entscheiden, dass Sie sich nicht darum kümmern. Auf diese Weise kann Ihr Code in einem separaten Thread ausgeführt werden, ohne den Kontext mit sich zu bringen.

Wie erreichen Sie das? Standardmäßig geht der await a;Code davon aus, dass Sie den Kontext erfassen und wiederherstellen möchten:

await a; //Same as the line below
await a.ConfigureAwait(true);

Wenn Sie zulassen möchten, dass der Hauptcode in einem neuen Thread ohne den ursprünglichen Kontext fortgesetzt wird, verwenden Sie einfach false anstelle von true, damit der Kontext nicht wiederhergestellt werden muss.

await a.ConfigureAwait(false);

Nachdem das Programm angehalten wurde, wird es möglicherweise in einem völlig anderen Thread mit einem anderen Kontext fortgesetzt . Hierher würde die Leistungsverbesserung kommen - sie könnte auf jedem verfügbaren Thread fortgesetzt werden, ohne dass der ursprüngliche Kontext wiederhergestellt werden muss, mit dem sie begonnen hat.

Ist das Zeug verwirrend? Hölle ja! Kannst du es herausfinden? Wahrscheinlich! Wenn Sie die Konzepte verstanden haben, fahren Sie mit Stephen Clearys Erklärungen fort, die sich eher an jemanden richten, der ein technisches Verständnis von async/ awaitbereits hat.


Nehmen wir an, wenn alle diese Aufgaben ein int zurückgeben und ich das Ergebnis der ersten Aufgabe in der zweiten Aufgabe (oder einer Berechnung) verwende. Wäre das falsch?
Veerendra Gupta

3
@ veerendragupta ja. In diesem Fall sollten Sie sie bewusst nicht asynchron ausführen (da sie nicht asynchron sind). Es gibt noch ein paar andere Dinge in Bezug auf den Konfigurationskontext, auf die ich hier nicht eingehen werde
Joe Phillips

Also await MethodCall()ist eine absolute Verschwendung? Sie können das await/ genauso gut fallen lassen async?
Vitani

2
@ Jocie Nicht ganz. Wenn Sie anrufen await, wird der Thread meiner Meinung nach wieder in den Pool freigegeben, anstatt ihn zu halten. Dies macht es für andere Zwecke verfügbar, bis die Aufgabe zurückgegeben wird
Joe Phillips

2
@ JoePhillips Ich denke, was Sie gerade gesagt haben, ist die Essenz von Async / Warten. Der aufrufende Thread wird freigegeben und kann von anderen Prozessen auf dem Computer verwendet werden. Wenn der Warteanruf abgeschlossen ist, wird ein neuer Thread verwendet, um das fortzusetzen, was der Anrufer ursprünglich gestartet hat. Der Anrufer wartet noch, aber der Vorteil ist, dass in der Zwischenzeit ein Thread freigegeben wird. Das ist der Vorteil von Async / Wait?
Bob Horn

147

Schauen Sie sich neben den anderen Antworten auch das Warten an (C # -Referenz).

und genauer gesagt im beigefügten Beispiel wird Ihre Situation ein wenig erklärt

Das folgende Windows Forms-Beispiel veranschaulicht die Verwendung von await in einer asynchronen Methode, WaitAsynchronouslyAsync. Vergleichen Sie das Verhalten dieser Methode mit dem Verhalten von WaitSynchronously. Ohne einen auf eine Aufgabe angewendeten Warteoperator wird WaitSynchron trotz der Verwendung des Async-Modifikators in seiner Definition und eines Aufrufs von Thread.Sleep in seinem Text synchron ausgeführt.

private async void button1_Click(object sender, EventArgs e)
{
    // Call the method that runs asynchronously.
    string result = await WaitAsynchronouslyAsync();

    // Call the method that runs synchronously.
    //string result = await WaitSynchronously ();

    // Display the result.
    textBox1.Text += result;
}

// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
    await Task.Delay(10000);
    return "Finished";
}

// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
    // Add a using directive for System.Threading.
    Thread.Sleep(10000);
    return "Finished";
}

3
Danke für die Antwort. Aber wird WaitAsynchronouslyAsync () in einem separaten Thread ausgeführt?
Dan Dinu

32
Ich glaube also, aus dem Abschnitt Ein
Adriaan Stander

13
In diesem MSDN-Artikel heißt es: "Die Schlüsselwörter async und await führen nicht dazu, dass zusätzliche Threads erstellt werden. Eine asynchrone Methode wird nicht in einem eigenen Thread ausgeführt." Nach meinem Verständnis überspringt das Framework beim Warten auf Schlüsselwörter (zurück zum Aufrufer), damit der gesamte mögliche unabhängige Code ausgeführt werden kann, während auf lange Vorgänge gewartet wird. Ich denke, das bedeutet, dass, sobald der gesamte unabhängige Code ausgeführt wurde und die lange Operation nicht zurückgegeben wurde, sie blockiert wird. Ich lerne das jetzt gerade.
Mumm

9
@astander Das ist falsch. Es wird nicht auf einem anderen Thread ausgeführt. Es wird nur die Fortsetzung (der Rest der Methode) geplant, die aufgerufen werden soll, wenn der von Task.DelayFeuern verwendete Timer ausgelöst wird .
MgSam

1
Diese Antwort ist wegen des Schlafes falsch. Siehe die akzeptierte Antwort mit warten Task.Delay (1000); das hat das richtige Verhalten.
Jared Updike

62

Zeigen Sie die obigen Erklärungen in Aktion in einem einfachen Konsolenprogramm:

class Program
{
    static void Main(string[] args)
    {
        TestAsyncAwaitMethods();
        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }

    public async static void TestAsyncAwaitMethods()
    {
        await LongRunningMethod();
    }

    public static async Task<int> LongRunningMethod()
    {
        Console.WriteLine("Starting Long Running method...");
        await Task.Delay(5000);
        Console.WriteLine("End Long Running method...");
        return 1;
    }
}

Und die Ausgabe ist:

Starting Long Running method...
Press any key to exit...
End Long Running method...

Somit,

  1. Main startet die Langzeitmethode über TestAsyncAwaitMethods. Das kehrt sofort zurück, ohne den aktuellen Thread anzuhalten, und es wird sofort die Meldung "Zum Beenden eine beliebige Taste drücken" angezeigt
  2. Währenddessen LongRunningMethodläuft das im Hintergrund. Sobald dies abgeschlossen ist, greift ein anderer Thread aus Threadpool diesen Kontext auf und zeigt die endgültige Nachricht an

Somit wird kein Thread blockiert.


"Zum Beenden eine beliebige Taste drücken ..." wird in welchem ​​Teil der Ausgabe angezeigt?
StudioX

1
und was nützt (return 1)? ist es nötig?
StudioX

1
@StudioX Ich denke, es muss Rückgabetyp Ganzzahl haben
Kuba Do

Ich denke, der return 1Teil verdient eine weitere Erklärung: Mit dem awaitSchlüsselwort können Sie den zugrunde liegenden Typ von Task<T>direkt zurückgeben, wodurch es einfacher wird, Ihren vorhandenen Code an die Welt des Wartens / Asynchronisierens anzupassen . Sie müssen jedoch keinen Wert zurückgeben, da es möglich ist, a zurückzugeben, Taskohne einen Rückgabetyp anzugeben, der einer synchronen voidMethode entspricht. Beachten Sie, dass C # async voidMethoden zulässt , aber Sie sollten dies vermeiden, es sei denn, Sie befassen sich mit Ereignishandlern.
Christiano Kiss

41

Ich denke, Sie haben mit ein schlechtes Beispiel gewählt System.Threading.Thread.Sleep

Der Zweck einer asyncAufgabe besteht darin, sie im Hintergrund ausführen zu lassen, ohne den Hauptthread zu sperren, zDownloadFileAsync

System.Threading.Thread.Sleep ist nichts, was "getan" wird, es schläft nur und daher ist Ihre nächste Zeile nach 5 Sekunden erreicht ...

Lesen Sie diesen Artikel, ich denke, es ist eine großartige Erklärung asyncund ein awaitKonzept: http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx


3
Warum Schlaf ein schlechtes Beispiel ist, aber Download ein gutes Beispiel ist. Es ist wie bei FooBar, wenn ich Thread sehe. Ich verstehe, dass es eine Aufgabe gibt, die einige Zeit in Anspruch nimmt. Ich denke, seine Frage ist relevant
Abdurrahim

1
@Abdurrahim Thread.Sleepblockiert den Thread (der Thread kann nichts anderes tun, als im Leerlauf zu sitzen), eine asynchrone Methode jedoch nicht. Im Fall von DownloadFileAsynckann der Thread etwas anderes tun, bis eine Antwort vom Remote-Server kommt. Ein besserer Platzhalter für "eine Aufgabe, die Zeit braucht" in einer Task.Delayasynchronen Methode ist , da dies tatsächlich asynchron ist.
Gabriel Luci

@ GabrielLuci Mein Einwand betrifft nicht Delay vs Sleep. Ihre Antwort sieht eher aus wie eine Strohmann-Antwort. Wenn Sie dies als Kommentar zu der Frage setzen, wäre das nichts, was ich beanstanden könnte, aber als Antwort riecht es eher nach einer Strohmann-Antwort. Ich denke, es ist immer noch in Ordnung, dort asynchron zu verwenden, selbst wenn alle Anrufe, die er / sie tätigen muss, Anrufe blockieren. Es wird nicht den ganzen Zweck ungültig machen ... Sogar alles, was übrig bleibt, wird syntaktischer Zucker sein, der als gültiger Fall gilt.
Abdurrahim

1
Das war nicht meine Antwort. Aber um Ihren Punkt anzusprechen: Es hängt vom Zweck der Methode ab. Wenn er nur eine Methode zum Aufrufen haben wollte, war er erfolgreich. In diesem Fall versuchte er jedoch, eine Methode zu erstellen, die asynchron ausgeführt wird. Er tat dies, indem er nur das asyncSchlüsselwort benutzte . Aber seine Methode lief immer noch synchron, und diese Antwort erklärte perfekt, warum: weil er eigentlich keinen asynchronen Code ausführte. Die markierten Methoden werden asyncimmer noch synchron ausgeführt, bis Sie awaitunvollständig sind Task. Wenn dies nicht awaitder Fall ist , wird die Methode synchron ausgeführt, und der Compiler warnt Sie davor.
Gabriel Luci

23

Hier ist ein schnelles Konsolenprogramm, um es den folgenden Personen klar zu machen. Die TaskToDoMethode ist Ihre lang laufende Methode, die Sie asynchronisieren möchten. Die asynchrone Ausführung erfolgt über die TestAsyncMethode. Die Testschleifenmethode führt die TaskToDoAufgaben nur durch und führt sie asynchron aus. Sie können dies in den Ergebnissen sehen, da sie nicht von Lauf zu Lauf in derselben Reihenfolge abgeschlossen werden. Sie werden nach Abschluss an den Konsolen-UI-Thread gemeldet. Einfach, aber ich denke, die simplen Beispiele bringen den Kern des Musters besser zur Geltung als die komplexeren Beispiele:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestingAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLoops();
            Console.Read();
        }

        private static async void TestLoops()
        {
            for (int i = 0; i < 100; i++)
            {
                await TestAsync(i);
            }
        }

        private static Task TestAsync(int i)
        {
            return Task.Run(() => TaskToDo(i));
        }

        private async static void TaskToDo(int i)
        {
            await Task.Delay(10);
            Console.WriteLine(i);
        }
    }
}

21

Für schnellstes Lernen ..

  • Verstehen Sie den Ablauf der Methodenausführung (mit einem Diagramm): 3 Minuten

  • Frage Selbstbeobachtung (Lernsache): 1 min

  • Schnell durch Syntax Zucker: 5 Minuten

  • Teilen Sie die Verwirrung eines Entwicklers: 5 Minuten

  • Problem: Ändern Sie schnell eine reale Implementierung von normalem Code in Async-Code: 2 Minuten

  • Wohin als nächstes?

Verstehen Sie den Ablauf der Methodenausführung (mit einem Diagramm): 3 Minuten

Konzentrieren Sie sich in diesem Bild nur auf # 6 (nichts weiter) Geben Sie hier die Bildbeschreibung ein

Bei Schritt 6: Die Ausführung wurde hier gestoppt, da die Arbeit ausgegangen ist. Um fortzufahren, benötigt es ein Ergebnis von getStringTask (eine Art Funktion). Daher verwendet es einen awaitOperator, um seinen Fortschritt auszusetzen und dem Aufrufer die Kontrolle (Ausbeute) zurückzugeben (dieser Methode, in der wir uns befinden). Der eigentliche Aufruf von getStringTask erfolgte früher in # 2. Bei # 2 wurde versprochen, ein String-Ergebnis zurückzugeben. Aber wann wird das Ergebnis zurückgegeben? Sollten wir (# 1: AccessTheWebAsync) erneut einen zweiten Anruf tätigen? Wer bekommt das Ergebnis, # 2 (aufrufende Anweisung) oder # 6 (wartende Anweisung)

Der externe Aufrufer von AccessTheWebAsync () wartet jetzt ebenfalls. Der Anrufer wartet also auf AccessTheWebAsync, und AccessTheWebAsync wartet im Moment auf GetStringAsync. Interessant ist, dass AccessTheWebAsync vor dem Warten einige Arbeiten ausgeführt hat (# 4), um möglicherweise Zeit vor dem Warten zu sparen. Die gleiche Freiheit für Multitasking steht auch dem externen Anrufer (und allen Anrufern in der Kette) zur Verfügung, und dies ist das größte Plus dieses asynchronen Dings! Sie haben das Gefühl, es ist synchron oder normal, aber es ist nicht so.

Denken Sie daran, dass die Methode bereits zurückgegeben wurde (# 2) und nicht erneut zurückgegeben werden kann (kein zweites Mal). Woher weiß der Anrufer das? Alles dreht sich um Aufgaben! Aufgabe wurde übergeben. Auf die Aufgabe wurde gewartet (keine Methode, kein Wert). Der Wert wird in Aufgabe festgelegt. Der Aufgabenstatus wird auf Abschluss gesetzt. Der Anrufer überwacht nur die Aufgabe (Nr. 6). 6 # ist also die Antwort darauf, wo / wer das Ergebnis erhält. Lesen Sie weiter für später hier .

Frage Selbstbeobachtung um des Lernens willen: 1 min

Lassen Sie uns die Frage etwas anpassen:

Wie und wann und ? asyncawait Tasks

Weil das Lernen Taskautomatisch die beiden anderen abdeckt (und Ihre Frage beantwortet)

Schnell durch Syntax Zucker: 5 Minuten

  • Vor der Konvertierung (ursprüngliche Methode)

    internal static int Method(int arg0, int arg1) { int result = arg0 + arg1; IO(); // Do some long running IO. return result; }

  • eine Task-ified-Methode zum Aufrufen der obigen Methode

    internal static Task<int> MethodTask(int arg0, int arg1) { Task<int> task = new Task<int>(() => Method(arg0, arg1)); task.Start(); // Hot task (started task) should always be returned. return task; }

Haben wir Erwarten oder Asynchronisieren erwähnt? Rufen Sie die obige Methode auf und Sie erhalten eine Aufgabe, die Sie überwachen können. Sie wissen bereits, was die Aufgabe zurückgibt. Eine Ganzzahl.

  • Das Aufrufen einer Aufgabe ist etwas schwierig, und dann werden die Schlüsselwörter angezeigt. Rufen wir MethodTask () auf

    internal static async Task<int> MethodAsync(int arg0, int arg1) { int result = await HelperMethods.MethodTask(arg0, arg1); return result; }

Gleicher Code oben hinzugefügt wie Bild unten: Geben Sie hier die Bildbeschreibung ein

  1. Wir warten darauf, dass die Aufgabe erledigt wird. Daher dieawait
  2. Da wir await verwenden, müssen wir verwenden async(obligatorische Syntax)
  3. MethodAsync mit Asyncals Präfix (Codierungsstandard)

awaitist leicht zu verstehen, aber die restlichen zwei ( async, Async) können nicht sein :). Nun, es sollte für den Compiler jedoch viel sinnvoller sein. Weitere Informationen für später hier

Es gibt also 2 Teile.

  1. 'Aufgabe' erstellen
  2. Erstellen Sie syntaktischen Zucker, um die Aufgabe aufzurufen ( await+async)

Denken Sie daran, wir hatten einen externen Anrufer für AccessTheWebAsync (), und dieser Anrufer wird auch nicht verschont ... dh er benötigt auch denselben await+async. Und die Kette geht weiter. Aber es wird immer ein TaskEnde geben.

Alles in Ordnung, aber ein Entwickler war überrascht, dass # 1 (Aufgabe) fehlte ...

Teilen Sie die Verwirrung eines Entwicklers: 5 Minuten

Ein Entwickler hat den Fehler gemacht, nicht zu implementieren, Taskaber es funktioniert immer noch! Versuchen Sie, die Frage und nur die akzeptierte Antwort zu verstehen , hier zur Verfügung gestellt . Ich hoffe, Sie haben gelesen und vollständig verstanden. Die Zusammenfassung ist, dass wir 'Task' möglicherweise nicht sehen / implementieren, es aber irgendwo in einer übergeordneten Klasse implementiert ist. Ebenso ist es in unserem Beispiel MethodAsync()viel einfacher , eine bereits erstellte Methode aufzurufen, als diese Methode mit a Task( MethodTask()) uns selbst zu implementieren. Den meisten Entwicklern fällt es schwer, sich Tasksbeim Konvertieren eines Codes in einen asynchronen Code zurechtzufinden.

Tipp: Versuchen Sie, eine vorhandene Async-Implementierung (wie MethodAsyncoder ToListAsync) zu finden, um die Schwierigkeit auszulagern. Wir müssen uns also nur mit Async befassen und warten (was einfach und dem normalen Code ziemlich ähnlich ist).

Problem: Ändern Sie schnell eine reale Implementierung von normalem Code in Async-Betrieb: 2 Minuten

Die unten in der Datenschicht gezeigte Codezeile begann zu brechen (an vielen Stellen). Weil wir einen Teil unseres Codes von .Net Framework 4.2. * Auf .Net Core aktualisiert haben. Wir mussten dies in 1 Stunde während der gesamten Anwendung beheben!

var myContract = query.Where(c => c.ContractID == _contractID).First();

kinderleicht!

  1. Wir haben das EntityFramework-Nuget-Paket installiert, da es QueryableExtensions enthält. Mit anderen Worten, es führt die Async-Implementierung (Aufgabe) durch, sodass wir mit einfachem Asyncund awaitin Code überlebendem Material überleben können .
  2. Namespace = Microsoft.EntityFrameworkCore

Die aufrufende Codezeile wurde wie folgt geändert

var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync();
  1. Methodensignatur geändert von

    Contract GetContract(int contractnumber)

    zu

    async Task<Contract> GetContractAsync(int contractnumber)

  2. Die aufrufende Methode war ebenfalls betroffen: GetContractAsync(123456);wurde als aufgerufenGetContractAsync(123456).Result;

  3. Wir haben es überall in 30 Minuten geändert!

Der Architekt sagte uns jedoch, wir sollten die EntityFramework-Bibliothek nicht nur dafür verwenden! Hoppla! Theater! Dann haben wir eine benutzerdefinierte Task-Implementierung (yuk) erstellt. Was du weißt wie. Immer noch einfach! ..noch yuk ..

Wohin als nächstes? Es gibt ein wunderbares kurzes Video über das Konvertieren synchroner Anrufe in asynchrone Anrufe in ASP.Net Core . Vielleicht ist dies wahrscheinlich die Richtung, in die man nach dem Lesen gehen würde.


fantastische Antwort! das hat mir eine
Menge

1
Gute Antwort. Vielleicht möchten Sie ein paar kleine Dinge beheben, wie: (a) Erwähnung von ".Net Framework 4.2" (keine mir bekannte Version existiert) (b) Gehäuse in EntityFrameWork => EntityFramework
immitev

15

Alle Antworten hier verwenden Task.Delay()oder eine andere eingebaute asyncFunktion. Aber hier ist mein Beispiel, das keine dieser asyncFunktionen verwendet:

// Starts counting to a large number and then immediately displays message "I'm counting...". 
// Then it waits for task to finish and displays "finished, press any key".
static void asyncTest ()
{
    Console.WriteLine("Started asyncTest()");
    Task<long> task = asyncTest_count();
    Console.WriteLine("Started counting, please wait...");
    task.Wait(); // if you comment this line you will see that message "Finished counting" will be displayed before we actually finished counting.
    //Console.WriteLine("Finished counting to " + task.Result.ToString()); // using task.Result seems to also call task.Wait().
    Console.WriteLine("Finished counting.");
    Console.WriteLine("Press any key to exit program.");
    Console.ReadLine();
}

static async Task<long> asyncTest_count()
{
    long k = 0;
    Console.WriteLine("Started asyncTest_count()");
    await Task.Run(() =>
    {
        long countTo = 100000000;
        int prevPercentDone = -1;
        for (long i = 0; i <= countTo; i++)
        {
            int percentDone = (int)(100 * (i / (double)countTo));
            if (percentDone != prevPercentDone)
            {
                prevPercentDone = percentDone;
                Console.Write(percentDone.ToString() + "% ");
            }

            k = i;
        }
    });
    Console.WriteLine("");
    Console.WriteLine("Finished asyncTest_count()");
    return k;
}

2
Vielen Dank! Die erste Antwort, die tatsächlich funktioniert, anstatt zu warten.
Jeffnl

Vielen Dank für das Zeigen task.Wait();und wie es verwendet werden kann, um Async / Warten auf die Hölle zu vermeiden: P
Encoder

12

Diese Antwort soll einige spezifische Informationen zu ASP.NET enthalten.

Durch die Verwendung von async / await im MVC-Controller ist es möglich, die Thread-Pool-Auslastung zu erhöhen und einen viel besseren Durchsatz zu erzielen, wie im folgenden Artikel erläutert.

http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

In Webanwendungen, in denen beim Start eine große Anzahl gleichzeitiger Anforderungen angezeigt wird oder die eine hohe Last aufweisen (bei der die Parallelität plötzlich zunimmt), erhöht das asynchrone Ausführen dieser Webdienstaufrufe die Reaktionsfähigkeit Ihrer Anwendung. Die Verarbeitung einer asynchronen Anforderung dauert genauso lange wie die einer synchronen Anforderung. Wenn eine Anforderung beispielsweise einen Webdienstaufruf ausführt, dessen Abschluss zwei Sekunden dauert, dauert die Anforderung zwei Sekunden, unabhängig davon, ob sie synchron oder asynchron ausgeführt wird. Während eines asynchronen Aufrufs wird ein Thread jedoch nicht daran gehindert, auf andere Anforderungen zu antworten, während er auf den Abschluss der ersten Anforderung wartet. Daher verhindern asynchrone Anforderungen das Anfordern von Warteschlangen und das Wachstum des Thread-Pools, wenn viele gleichzeitige Anforderungen vorhanden sind, die lang laufende Vorgänge aufrufen.


12

Async & Await Einfache Erklärung

Einfache Analogie

Eine Person kann auf ihren Morgenzug warten . Dies ist alles, was sie tun, da dies ihre Hauptaufgabe ist, die sie derzeit ausführen. (Synchrone Programmierung (was Sie normalerweise tun!))

Eine andere Person wartet möglicherweise auf ihren Morgenzug, während sie eine Zigarette raucht und dann ihren Kaffee trinkt. (Asynchrone Programmierung)

Was ist asynchrone Programmierung?

Bei der asynchronen Programmierung wählt ein Programmierer einen Teil seines Codes in einem vom Hauptthread der Ausführung getrennten Thread aus und benachrichtigt den Hauptthread nach dessen Abschluss.

Was macht das asynchrone Schlüsselwort eigentlich?

Stellen Sie das asynchrone Schlüsselwort einem Methodennamen wie vor

async void DoSomething(){ . . .

Ermöglicht dem Programmierer, das Schlüsselwort await beim Aufrufen asynchroner Aufgaben zu verwenden. Das ist alles was es tut.

Warum ist das wichtig?

In vielen Softwaresystemen ist der Hauptthread für Operationen reserviert, die sich speziell auf die Benutzeroberfläche beziehen. Wenn ich einen sehr komplexen rekursiven Algorithmus ausführe, dessen Fertigstellung auf meinem Computer 5 Sekunden dauert, diesen jedoch im Hauptthread (UI-Thread) ausführt. Wenn der Benutzer versucht, auf etwas in meiner Anwendung zu klicken, wird er eingefroren angezeigt da mein Haupt-Thread in die Warteschlange gestellt wurde und derzeit viel zu viele Operationen verarbeitet. Infolgedessen kann der Hauptthread den Mausklick nicht verarbeiten, um die Methode über den Schaltflächenklick auszuführen.

Wann verwenden Sie Async und Await?

Verwenden Sie die asynchronen Schlüsselwörter idealerweise, wenn Sie etwas tun, das nicht die Benutzeroberfläche betrifft.

Nehmen wir also an, Sie schreiben ein Programm, mit dem der Benutzer auf seinem Mobiltelefon skizzieren kann, das jedoch alle 5 Sekunden das Wetter im Internet überprüft.

Wir sollten auf den Anruf warten, den die Abrufanrufe alle 5 Sekunden an das Netzwerk senden, um das Wetter zu ermitteln, da der Benutzer der Anwendung weiterhin mit dem mobilen Touchscreen interagieren muss, um hübsche Bilder zu zeichnen.

Wie verwenden Sie Async und Await?

Im Anschluss an das obige Beispiel finden Sie hier einen Pseudocode zum Schreiben:

    //ASYNCHRONOUS
    //this is called using the await keyword every 5 seconds from a polling timer or something.

    async Task CheckWeather()
    {
        var weather = await GetWeather();
        //do something with the weather now you have it
    }

    async Task<WeatherResult> GetWeather()
    {

        var weatherJson = await CallToNetworkAddressToGetWeather();
        return deserializeJson<weatherJson>(weatherJson);
    }

    //SYNCHRONOUS
    //This method is called whenever the screen is pressed
    void ScreenPressed()
    {
        DrawSketchOnScreen();
    }

Zusätzliche Hinweise - Update

Ich habe vergessen, in meinen ursprünglichen Notizen zu erwähnen, dass Sie in C # nur Methoden abwarten können, die in Aufgaben eingeschlossen sind. Zum Beispiel können Sie diese Methode abwarten:

// awaiting this will return a string.
// calling this without await (synchronously) will result in a Task<string> object.
async Task<string> FetchHelloWorld() {..

Sie können nicht auf Methoden warten, die keine Aufgaben wie diese sind:

async string FetchHelloWorld() {..

Fühlen Sie sich frei , den Quellcode für die Task - Klasse zu überprüfen hier .


4
Vielen Dank, dass Sie sich die Zeit genommen haben, dieses zu schreiben.
Prashant

10

Async / Warten

Tatsächlich sind Async / Await ein Schlüsselwortpaar, das nur syntaktischer Zucker zum Erstellen eines Rückrufs einer asynchronen Aufgabe ist.

Nehmen Sie diese Operation als Beispiel:

    public static void DoSomeWork()
    {
        var task = Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS NOT bubbling up due to the different threads
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // This is the callback
        task.ContinueWith((t) => {
            // -> Exception is swallowed silently
            Console.WriteLine("Completed");

            // [RUNS ON WORKER THREAD]
        });
    }

Der obige Code hat mehrere Nachteile. Fehler werden nicht weitergegeben und sind schwer zu lesen. Aber Async und Await kommen herein, um uns zu helfen:

    public async static void DoSomeWork()
    {
        var result = await Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS bubbling up
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // every thing below is a callback 
        // (including the calling methods)

        Console.WriteLine("Completed");

    }

Warte-Aufrufe müssen in Async-Methoden erfolgen. Dies hat einige Vorteile:

  • Gibt das Ergebnis der Aufgabe zurück
  • Erstellt automatisch einen Rückruf
  • prüft auf Fehler und lässt sie im Callstack sprudeln (nur bis zu nicht erwartete Anrufe im Callstack)
  • wartet auf das Ergebnis
  • macht den Hauptfaden frei
  • führt den Rückruf auf dem Hauptthread aus
  • verwendet einen Arbeitsthread aus dem Threadpool für die Aufgabe
  • macht den Code leicht lesbar
  • und vieles mehr

HINWEIS : Async und Await werden verwendet , mit asynchronen Aufrufen nicht diese zu machen. Sie müssen hierfür Task Libary verwenden, z. B. Task.Run ().

Hier ist ein Vergleich zwischen erwarteten und keinen erwarteten Lösungen

Dies ist die nicht asynchrone Lösung:

    public static long DoTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]
        var task = Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // goes directly further
        // WITHOUT waiting until the task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 50 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

Dies ist die asynchrone Methode:

    public async static Task<long> DoAwaitTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]

        await Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // Waits until task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 2050 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

Sie können eine asynchrone Methode ohne das Schlüsselwort await aufrufen. Dies bedeutet jedoch, dass alle hier aufgeführten Ausnahmen im Freigabemodus verschluckt werden:

    public static Stopwatch stopWatch { get; } = new Stopwatch();

    static void Main(string[] args)
    {
        Console.WriteLine("DoAwaitTask: " + DoAwaitTask().Result + " ms");
        // 2050 (2000 more because of the await)
        Console.WriteLine("DoTask: " + DoTask() + " ms");
        // 50
        Console.ReadKey();
    }

Async und Await sind nicht für paralleles Rechnen gedacht. Sie werden verwendet, um Ihren Haupt-Thread nicht zu blockieren. Wenn es um asp.net- oder Windows-Anwendungen geht, ist es eine schlechte Sache, den Hauptthread aufgrund eines Netzwerkaufrufs zu blockieren. Wenn Sie dies tun, reagiert Ihre App nicht mehr oder stürzt sogar ab.

Weitere Beispiele finden Sie in den ms-Dokumenten .


9

Um ehrlich zu sein, denke ich immer noch, dass die beste Erklärung die Zukunft und die Versprechen auf Wikipedia ist: http://en.wikipedia.org/wiki/Futures_and_promises

Die Grundidee ist, dass Sie einen separaten Pool von Threads haben, die Aufgaben asynchron ausführen. Bei der Verwendung. Das Objekt verspricht jedoch, dass es die Operation irgendwann ausführen wird, und gibt Ihnen das Ergebnis, wenn Sie es anfordern. Dies bedeutet, dass es blockiert wird, wenn Sie das Ergebnis anfordern und noch nicht fertig sind, aber ansonsten im Thread-Pool ausgeführt wird.

Von dort aus können Sie Dinge optimieren: Einige Vorgänge können asynchron implementiert werden, und Sie können Dinge wie Datei-E / A und Netzwerkkommunikation optimieren, indem Sie nachfolgende Anforderungen zusammenfassen und / oder neu anordnen. Ich bin mir nicht sicher, ob dies bereits im Task-Framework von Microsoft enthalten ist - aber wenn dies nicht der Fall ist, wäre dies eines der ersten Dinge, die ich hinzufügen würde.

Sie können das zukünftige Muster tatsächlich mit Erträgen in C # 4.0 implementieren. Wenn Sie wissen möchten, wie es genau funktioniert, kann ich diesen Link empfehlen, der gute Arbeit leistet: http://code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/ . Wenn Sie jedoch selbst damit spielen, werden Sie feststellen, dass Sie wirklich Sprachunterstützung benötigen, wenn Sie all die coolen Dinge tun möchten - genau das hat Microsoft getan.


8

In dieser Geige https://dotnetfiddle.net/VhZdLU (und wenn möglich verbessern) können Sie eine einfache Konsolenanwendung ausführen, die die Verwendung von Task, Task.WaitAll (), asynchronen und wartenden Operatoren im selben Programm anzeigt .

Diese Geige sollte Ihr Ausführungszykluskonzept klären.

Hier ist der Beispielcode

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {               
        var a = MyMethodAsync(); //Task started for Execution and immediately goes to Line 19 of the code. Cursor will come back as soon as await operator is met       
        Console.WriteLine("Cursor Moved to Next Line Without Waiting for MyMethodAsync() completion");
        Console.WriteLine("Now Waiting for Task to be Finished");       
        Task.WaitAll(a); //Now Waiting      
        Console.WriteLine("Exiting CommandLine");       
    }

    public static async Task MyMethodAsync()
    {
        Task<int> longRunningTask = LongRunningOperation();
        // independent work which doesn't need the result of LongRunningOperationAsync can be done here
        Console.WriteLine("Independent Works of now executes in MyMethodAsync()");
        //and now we call await on the task 
        int result = await longRunningTask;
        //use the result 
        Console.WriteLine("Result of LongRunningOperation() is " + result);
    }

    public static async Task<int> LongRunningOperation() // assume we return an int from this long running operation 
    {
        Console.WriteLine("LongRunningOperation() Started");
        await Task.Delay(2000); // 2 second delay
        Console.WriteLine("LongRunningOperation() Finished after 2 Seconds");
        return 1;
    }   

}

Trace aus dem Ausgabefenster: Geben Sie hier die Bildbeschreibung ein


3
public static void Main(string[] args)
{
    string result = DownloadContentAsync().Result;
    Console.ReadKey();
}

// You use the async keyword to mark a method for asynchronous operations.
// The "async" modifier simply starts synchronously the current thread. 
// What it does is enable the method to be split into multiple pieces.
// The boundaries of these pieces are marked with the await keyword.
public static async Task<string> DownloadContentAsync()// By convention, the method name ends with "Async
{
    using (HttpClient client = new HttpClient())
    {
        // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished.
        // If it is already finished, the method continues to run synchronously.
        // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed.


        // Http request example. 
        // (In this example I can set the milliseconds after "sleep=")
        String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000");

        Console.WriteLine(result);

        // After completing the result response, the state machine will continue to synchronously execute the other processes.


        return result;
    }
}

3

Auf einer höheren Ebene:

1) Das Schlüsselwort Async aktiviert das Warten und das ist alles, was es tut. Das Schlüsselwort Async führt die Methode nicht in einem separaten Thread aus. Die asynchrone Anfangsmethode wird synchron ausgeführt, bis sie auf eine zeitaufwändige Aufgabe wartet.

2) Sie können auf eine Methode warten, die Task oder Task vom Typ T zurückgibt. Sie können nicht auf eine asynchrone void-Methode warten.

3) In dem Moment, in dem Hauptthread-Begegnungen auf eine zeitaufwändige Aufgabe warten oder wenn die eigentliche Arbeit gestartet wird, kehrt der Hauptthread zum Aufrufer der aktuellen Methode zurück.

4) Wenn der Hauptthread auf eine Aufgabe wartet, die noch ausgeführt wird, wartet er nicht darauf und kehrt zum Aufrufer der aktuellen Methode zurück. Auf diese Weise bleibt die Anwendung reaktionsschnell.

5) Warten auf Verarbeitungsaufgabe, wird jetzt auf einem vom Thread-Pool getrennten Thread ausgeführt.

6) Wenn diese Warteaufgabe abgeschlossen ist, wird der gesamte darunter liegende Code vom separaten Thread ausgeführt

Unten ist der Beispielcode. Führen Sie es aus und überprüfen Sie die Thread-ID

using System;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAwaitDemo
{
    class Program
    {
        public static async void AsynchronousOperation()
        {
            Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            //Task<int> _task = AsyncMethod();
            int count = await AsyncMethod();

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            //int count = await _task;

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            DependentMethod(count);

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        }

        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine("Inside AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            int count = 0;

            await Task.Run(() =>
            {
                Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(20000);
                count = 10;
            });

            Console.WriteLine("Completed AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            return count;
        }       

        public static void DependentMethod(int count)
        {
            Console.WriteLine("Inside DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId + ". Total count is " + count);
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Started Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            AsynchronousOperation();

            Console.WriteLine("Completed Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            Console.ReadKey();
        }

    }
}

2

So wie ich es verstehe, sollte der Mischung auch ein dritter Begriff hinzugefügt werden : Task.

Async ist nur ein Qualifikationsmerkmal, das Sie Ihrer Methode hinzufügen, um zu sagen, dass es sich um eine asynchrone Methode handelt.

Taskist die Rückgabe der asyncFunktion. Es wird asynchron ausgeführt.

Sie awaiteine Aufgabe. Wenn die Codeausführung diese Zeile erreicht, springt die Steuerung zurück zum Aufrufer Ihrer umgebenden ursprünglichen Funktion.

Wenn Sie stattdessen die Rückgabe einer asyncFunktion (dh Task) einer Variablen zuweisen und die Codeausführung diese Zeile erreicht, wird sie in der umgebenden Funktion nur über diese Zeile hinaus fortgesetzt , während die Taskasynchrone Ausführung ausgeführt wird.


1

Ist ihre Verwendung gleichbedeutend mit dem Laichen von Hintergrund-Threads, um eine Logik mit langer Dauer auszuführen?

Dieser Artikel MDSN: Asynchrone Programmierung mit asynchronem und warten (C #) erklärt es explizit:

Die Schlüsselwörter async und await führen nicht dazu, dass zusätzliche Threads erstellt werden. Asynchrone Methoden erfordern kein Multithreading, da eine asynchrone Methode nicht in einem eigenen Thread ausgeführt wird. Die Methode wird im aktuellen Synchronisationskontext ausgeführt und verwendet die Zeit im Thread nur, wenn die Methode aktiv ist.


1

Im folgenden Code gibt die HttpClient-Methode GetByteArrayAsync die Aufgabe getContentsTask zurück. Die Aufgabe ist ein Versprechen, das tatsächliche Byte-Array zu erzeugen, wenn die Aufgabe abgeschlossen ist. Der Operator await wird auf getContentsTask angewendet, um die Ausführung in SumPageSizesAsync anzuhalten, bis getContentsTask abgeschlossen ist. In der Zwischenzeit wird die Kontrolle an den Aufrufer von SumPageSizesAsync zurückgegeben. Wenn getContentsTask beendet ist, wird der Ausdruck await zu einem Byte-Array ausgewertet.

private async Task SumPageSizesAsync()
{
    // To use the HttpClient type in desktop apps, you must include a using directive and add a 
    // reference for the System.Net.Http namespace.
    HttpClient client = new HttpClient();
    // . . .
    Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
    byte[] urlContents = await getContentsTask;

    // Equivalently, now that you see how it works, you can write the same thing in a single line.
    //byte[] urlContents = await client.GetByteArrayAsync(url);
    // . . .
}

1

Unten finden Sie Code, der die Excel-Datei durch Öffnen des Dialogfelds liest und dann asynchron verwendet. Warten Sie, bis der Code asynchron ausgeführt wird, der eine Zeile nach der anderen aus Excel liest und an das Raster bindet

namespace EmailBillingRates
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            lblProcessing.Text = "";
        }

        private async void btnReadExcel_Click(object sender, EventArgs e)
        {
            string filename = OpenFileDialog();

            Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
            Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename);
            Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
            Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
            try
            {
                Task<int> longRunningTask = BindGrid(xlRange);
                int result = await longRunningTask;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
            finally
            {
                //cleanup  
               // GC.Collect();
                //GC.WaitForPendingFinalizers();

                //rule of thumb for releasing com objects:  
                //  never use two dots, all COM objects must be referenced and released individually  
                //  ex: [somthing].[something].[something] is bad  

                //release com objects to fully kill excel process from running in the background  
                Marshal.ReleaseComObject(xlRange);
                Marshal.ReleaseComObject(xlWorksheet);

                //close and release  
                xlWorkbook.Close();
                Marshal.ReleaseComObject(xlWorkbook);

                //quit and release  
                xlApp.Quit();
                Marshal.ReleaseComObject(xlApp);
            }

        }

        private void btnSendEmail_Click(object sender, EventArgs e)
        {

        }

        private string OpenFileDialog()
        {
            string filename = "";
            OpenFileDialog fdlg = new OpenFileDialog();
            fdlg.Title = "Excel File Dialog";
            fdlg.InitialDirectory = @"c:\";
            fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*";
            fdlg.FilterIndex = 2;
            fdlg.RestoreDirectory = true;
            if (fdlg.ShowDialog() == DialogResult.OK)
            {
                filename = fdlg.FileName;
            }
            return filename;
        }

        private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange)
        {
            lblProcessing.Text = "Processing File.. Please wait";
            int rowCount = xlRange.Rows.Count;
            int colCount = xlRange.Columns.Count;

            // dt.Column = colCount;  
            dataGridView1.ColumnCount = colCount;
            dataGridView1.RowCount = rowCount;

            for (int i = 1; i <= rowCount; i++)
            {
                for (int j = 1; j <= colCount; j++)
                {
                    //write the value to the Grid  
                    if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
                    {
                         await Task.Delay(1);
                         dataGridView1.Rows[i - 1].Cells[j - 1].Value =  xlRange.Cells[i, j].Value2.ToString();
                    }

                }
            }
            lblProcessing.Text = "";
            return 0;
        }
    }

    internal class async
    {
    }
}

0

Die Antworten hier sind als allgemeine Anleitung zum Warten / Asynchronisieren nützlich. Sie enthalten auch einige Details darüber, wie das Warten / Asynchronisieren verkabelt ist. Ich möchte einige praktische Erfahrungen mit Ihnen teilen, die Sie kennen sollten, bevor Sie dieses Entwurfsmuster verwenden.

Der Begriff "Warten" ist wörtlich, sodass jeder Thread, den Sie aufrufen, auf das Ergebnis der Methode wartet, bevor Sie fortfahren. Im Vordergrund ist dies eine Katastrophe . Der Vordergrund-Thread trägt die Last der Erstellung Ihrer App, einschließlich Ansichten, Ansichtsmodellen, anfänglichen Animationen und allem, was Sie sonst noch mit diesen Elementen gebootet haben. Wenn Sie also auf den Vordergrund-Thread warten, stoppen Sie die App. Der Benutzer wartet und wartet, wenn nichts zu passieren scheint. Dies bietet eine negative Benutzererfahrung.

Sie können einen Hintergrund-Thread mit einer Vielzahl von Mitteln abwarten:

Device.BeginInvokeOnMainThread(async () => { await AnyAwaitableMethod(); });

// Notice that we do not await the following call, 
// as that would tie it to the foreground thread.
try
{
Task.Run(async () => { await AnyAwaitableMethod(); });
}
catch
{}

Der vollständige Code für diese Bemerkungen befindet sich unter https://github.com/marcusts/xamarin-forms-annoyances . Siehe die Lösung mit dem Namen AwaitAsyncAntipattern.sln.

Die GitHub-Site enthält auch Links zu einer ausführlicheren Diskussion zu diesem Thema.


1
Soweit ich async / awaitweiß , ist syntaktischer Zucker für Rückrufe nichts mit Threading zu tun. msdn.microsoft.com/en-us/magazine/hh456401.aspx Dies gilt für nicht CPU-gebundenen Code, z. B. Warten auf Eingabe oder Verzögerung. Task.Runsollte nur für CPU-gebundenen Code verwendet werden blog.stephencleary.com/2013/10/…
geometrikal

The term "await" is literal, so whatever thread you call it on will wait for the result of the method before continuing.Dies ist nicht wahr - vielleicht meinten Sie Task.Wait ()? Wenn Sie verwenden await, wird der Rest der Methode als Fortsetzung festgelegt, die ausgeführt werden soll, wenn alles, was Sie erwartet haben, abgeschlossen ist. Es beendet die Methode, in der Sie es verwendet haben, damit der Anrufer fortfahren kann. Wenn die erwartete Zeile dann tatsächlich vollständig ist, wird der Rest dieser Methode für einen Thread (normalerweise einen Arbeitsthread) abgeschlossen.
Don Cheadle

Bei @geometrikal geht es im Kern async/awaitdarum , .NET-Threads freizugeben . Wenn Sie awaiteine wirklich asynchrone Operation ausführen (z. B. File.WriteAsync von .NET), wird der Rest der von Ihnen verwendeten Methode angehalten await, sodass der Aufrufer fortfahren und möglicherweise seinen Zweck beenden kann. Es wird kein Thread blockiert oder auf die awaitOperation -ed gewartet . Wenn der von Ihnen awaitbearbeitete Vorgang abgeschlossen ist, wird der Rest der async/awaitMethode in einen Thread gestellt und ausgeführt (ähnlich einer Rückrufidee).
Don Cheadle
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.