wake-up-neo.com

Was kann die neue C # -Wartefunktion?

Kann mir jemand erklären, was die Funktion await bewirkt?

82
Chris Nicol

Sie haben gerade haben darüber bei PDC gesprochen gestern!

Await wird in Verbindung mit Tasks (parallele Programmierung) in .NET verwendet. Es ist ein Schlüsselwort, das in der nächsten Version von .NET eingeführt wird. Auf diese Weise können Sie die Ausführung einer Methode mehr oder weniger anhalten, um zu warten, bis die Task vollständig ausgeführt wurde. Hier ist ein kurzes Beispiel:

//create and run a new task  
Task<DataTable> dataTask = new Task<DataTable>(SomeCrazyDatabaseOperation);

//run some other code immediately after this task is started and running  
ShowLoaderControl();  
StartStoryboard();

//this will actually "pause" the code execution until the task completes.  It doesn't lock the thread, but rather waits for the result, similar to an async callback  
// please so also note, that the task needs to be started before it can be awaited. Otherwise it will never return
dataTask.Start();
DataTable table = await dataTask;

//Now we can perform operations on the Task result, as if we're executing code after the async operation completed  
listBoxControl.DataContext = table;  
StopStoryboard();  
HideLoaderControl();
61
RTigger

Grundsätzlich können Sie mit den Schlüsselwörtern async und await festlegen, dass die Ausführung einer Methode bei allen Verwendungen von await, die asynchrone Methodenaufrufe kennzeichnen, gestoppt werden soll Der asynchrone Betrieb ist abgeschlossen. Auf diese Weise können Sie eine Methode im Hauptthread einer App aufrufen und komplexe Arbeiten asynchron verarbeiten, ohne explizit Threads und Joins definieren oder den Hauptthread der App blockieren zu müssen.

Stellen Sie sich das so vor, als wäre es einer yield return - Anweisung in einer Methode ähnlich, die eine IEnumerable erzeugt. Wenn die Laufzeit den Wert yield erreicht, speichert sie im Grunde den aktuellen Status der Methode und gibt den Wert oder die Referenz zurück, die ausgegeben wird. Beim nächsten Aufruf von IEnumerator.MoveNext () für das Rückgabeobjekt (das intern von der Laufzeit generiert wird) wird der alte Zustand der Methode im Stapel wiederhergestellt und die Ausführung mit der nächsten Zeile nach dem yield return Fortgesetzt wir hatten die Methode nie verlassen. Ohne dieses Schlüsselwort muss ein IEnumerator-Typ benutzerdefiniert werden, um den Status zu speichern und die Iterationsanforderungen zu verarbeiten, wobei Methoden sehr komplex werden können.

Ebenso muss eine als async markierte Methode mindestens ein await haben. Auf einem await speichert die Laufzeit den aktuellen Thread-Status und den Aufrufstapel, führt den asynchronen Aufruf durch und kehrt zur Nachrichtenschleife der Laufzeit zurück, um die nächste Nachricht zu verarbeiten und die App reaktionsfähig zu halten. Wenn der asynchrone Vorgang abgeschlossen ist, wird bei der nächsten Zeitplanungsmöglichkeit der Aufrufstapel zum Hochfahren des asynchronen Vorgangs zurückgeschoben und fortgesetzt, als ob der Aufruf synchron wäre.

Diese beiden neuen Schlüsselwörter vereinfachen also die Codierung asynchroner Prozesse, ähnlich wie yield return Die Generierung benutzerdefinierter Enumerables vereinfacht hat. Mit ein paar Stichwörtern und ein wenig Hintergrundwissen können Sie alle verwirrenden und häufig fehleranfälligen Details eines traditionellen asynchronen Musters überspringen. Dies wird in so ziemlich jeder ereignisgesteuerten GUI-App wie Winforms, WPF von Silverlight, UNVERWERTBAR sein.

47
KeithS

Die aktuell akzeptierte Antwort ist irreführend. await hält nichts an. Erstens kann es nur in Methoden oder Lambdas verwendet werden, die als async markiert sind und Task oder void zurückgeben, wenn Sie nicht möchten, dass die Task -Instanz ausgeführt wird in dieser Methode.

Hier ist eine Illustration:

internal class Program
{
    private static void Main(string[] args)
    {
        var task = DoWork();
        Console.WriteLine("Task status: " + task.Status);
        Console.WriteLine("Waiting for ENTER");
        Console.ReadLine();
    }

    private static async Task DoWork()
    {
        Console.WriteLine("Entered DoWork(). Sleeping 3");
        // imitating time consuming code
        // in a real-world app this should be inside task, 
        // so method returns fast
        Thread.Sleep(3000);

        await Task.Run(() =>
            {
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("async task iteration " + i);
                    // imitating time consuming code
                    Thread.Sleep(1000);
                }
            });

        Console.WriteLine("Exiting DoWork()");
    }
}

Ausgabe:

Eingegebene DoWork (). Schlafen 3
asynchrone Task-Iteration 0
Aufgabenstatus: WaitingForActivation
Warten auf ENTER
asynchrone Task-Iteration 1
asynchrone Task-Iteration 2
asynchrone Task-Iteration 3
asynchrone Task-Iteration 4
asynchrone Task-Iteration 5
asynchrone Task-Iteration 6
asynchrone Task-Iteration 7
asynchrone Task-Iteration 8
asynchrone Task-Iteration 9
DoWork beenden ()

31
Anri

Für alle, die noch nicht mit asynchroner Programmierung in .NET vertraut sind, ist hier eine (völlig falsche) Analogie in einem Szenario, mit dem Sie möglicherweise besser vertraut sind - AJAX Aufrufe mit JavaScript/jQuery. Eine einfache jQuery AJAX Post sieht so aus:

$.post(url, values, function(data) {
  // AJAX call completed, do something with returned data here
});

Der Grund, warum wir die Ergebnisse in einer Rückruffunktion verarbeiten, ist, dass wir den aktuellen Thread nicht blockieren, während wir auf die Rückgabe des AJAX= Aufrufs warten. Nur wenn die Antwort fertig ist, wird der Rückruf ausgelöst. Freigabe des aktuellen Threads für andere Aufgaben in der Zwischenzeit.

Wenn nun JavaScript das Schlüsselwort await unterstützt (was es natürlich nicht tut ( noch! )), könnten Sie dasselbe damit erreichen:

var data = await $.post(url, values);
// AJAX call completed, do something with returned data here

Das ist viel sauberer, aber es sieht so aus, als hätten wir synchronen, blockierenden Code eingeführt. Aber der (gefälschte) JavaScript-Compiler hätte alles nach await genommen und in einen Callback verwandelt, sodass sich das zweite Beispiel zur Laufzeit genauso verhält wie das erste.

Es mag nicht so aussehen, als würde Ihnen viel Arbeit erspart, aber wenn es um Ausnahmebehandlung und Synchronisationskontexte geht, erledigt der Compiler tatsächlich eine Menge schweres Heben für Sie. Für mehr empfehle ich FAQs gefolgt von Stephen Clearys Blogserie .

10
Todd Menier