Was macht die neue Funktion zum Warten auf C #? [geschlossen]

82

Kann jemand erklären, was die awaitFunktion tut?

Chris Nicol
quelle
1
Beziehen Sie sich darauf? Asynchrone Programmierung in C # 5.0 Teil 2: Woher warten?
Jordanbtucker
Schöne Beispiele auch unter dotnetperls.com/async .
Miljen Mikic
Ich glaube nicht, dass diese Frage zu weit gefasst ist oder geschlossen werden sollte. Es wird gefragt, was ein Schlüsselwort bedeutet. (War eine frühere Version irgendwie anders?)
Panzercrisis

Antworten:

62

Sie haben gestern bei PDC darüber gesprochen !

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. Sie können damit mehr oder weniger die Ausführung einer Methode "anhalten", um darauf zu warten, dass die Task die Ausführung abschließt. 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();
RTigger
quelle
2
Wann ist es die C # Form der Versprechen: en.wikipedia.org/wiki/Futures_and_promises
Gorgen
12
Klingt sehr nach Thread.Join ().
Steve Guidi
10
Erinnert mich an COMEFROM
Joel Spolsky
20
Der Vollständigkeit halber fügen wir hinzu, dass der obige Code in eine Methode eingeschlossen werden muss, die mit einem asynchronen Schlüsselwort geschmückt ist. Diese Methode wird sofort zurückgegeben, sobald das erste wartende Schlüsselwort darin gefunden wird.
Przemek
14
In Ihren Worten: Sie können die Methode "anhalten", aber es sollte beachtet werden, dass der Thread nicht angehalten oder blockiert wird.
Matt Crinklaw-Vogt
47

Grundsätzlich sind die asyncund awaiterlauben Keywords , die Sie , dass die Ausführung eines Verfahrens angeben sollten auf allen Nutzungen stoppen await, die asynchrone Methodenaufrufe zu markieren, und dann wieder auf, sobald die asynchrone Operation abgeschlossen ist. Auf diese Weise können Sie eine Methode im Hauptthread einer App aufrufen und komplexe Arbeiten asynchron ausführen, ohne Threads und Verknüpfungen explizit definieren oder den Hauptthread der App blockieren zu müssen.

yield returnStellen Sie sich vor, es ähnelt einer Aussage in einer Methode, die eine IEnumerable erzeugt. Wenn die Laufzeit die erreicht yield, wird im Grunde der aktuelle Status der Methode gespeichert und der Wert oder die Referenz zurückgegeben, die ausgegeben werden. Wenn IEnumerator.MoveNext () das nächste Mal für das Rückgabeobjekt aufgerufen wird (das intern von der Laufzeit generiert wird), wird der alte Status der Methode im Stapel wiederhergestellt und die Ausführung mit der nächsten Zeile nach dem fortgesetzt, yield returnals hätten wir das Objekt nie verlassen Methode. Ohne dieses Schlüsselwort muss ein IEnumerator-Typ benutzerdefiniert definiert werden, um den Status zu speichern und die Iterationsanforderungen zu verarbeiten. Die Methoden können in der Tat SEHR komplex werden.

Ebenso muss eine als gekennzeichnete Methode asyncmindestens eine haben await. In einem awaitFall speichert die Laufzeit den Status und den Aufrufstapel des aktuellen Threads, 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 Planungsmöglichkeit der Aufrufstapel zum Hochfahren des asynchronen Vorgangs zurückgeschoben und fortgesetzt, als ob der Anruf synchron wäre.

Diese beiden neuen Schlüsselwörter vereinfachen die Codierung asynchroner Prozesse im Wesentlichen, ähnlich wie yield returndie Generierung benutzerdefinierter Aufzählungen. Mit ein paar Schlüsselwörtern und ein wenig Hintergrundwissen können Sie alle verwirrenden und oft fehleranfälligen Details eines herkömmlichen asynchronen Musters überspringen. Dies ist in so ziemlich jeder ereignisgesteuerten GUI-App wie Winforms, WPF of Silverlight von unschätzbarem Wert.

KeithS
quelle
31

Die aktuell akzeptierte Antwort ist irreführend. awaitpausiert nichts. Erstens kann es nur in Methoden oder Lambdas verwendet werden, die als asyncund als zurückgegeben markiert sind , Taskoder voidwenn es Ihnen egal ist, ob eine TaskInstanz in dieser Methode ausgeführt wird.

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:

DoWork () eingegeben. Sleeping 3
Async Task Iteration 0
Task Status: WaitingForActivation
Warten auf ENTER
Async Task Iteration 1
Async Task Iteration 2
Async Task Iteration 3
Async Task Iteration 4
Async Task Iteration 5
Async Task Iteration 6
Async Task Iteration 7
Async Task Iteration 8
Async Task Iteration 9
Beenden Arbeite()

Anri
quelle
1
Sie wissen auch, dass der Anrufer 3 Sekunden lang blockiert wird, bevor er überhaupt die Aufgabe erhält, die er ausführen kann await? Das heißt, wenn dies von einem UI-Thread aufgerufen wird, wird der UI-Thread für 3 Sekunden blockiert? Die Idee dieses Modells ist es, solche Dinge zu vermeiden.
Servy
1
@Servy ja, das war der Punkt. Alle Ausführungsstufen anzeigen. Dies ist kein reales Beispiel.
Anri
7
@Servy trollst du mich?
Anri
2
Nee. Ich versuche Ihnen zu helfen, Ihre Antwort zu verbessern.
Servy
2
@ Anri ... Ich schätze deine Bemühungen hier sehr. Vielen Dank!!
Praveen Prajapati
11

Für alle, die mit der asynchronen Programmierung in .NET noch nicht 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. Ein einfacher jQuery AJAX-Beitrag sieht folgendermaßen 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ückkehr des AJAX-Aufrufs warten. Erst wenn die Antwort fertig ist, wird der Rückruf ausgelöst und der aktuelle Thread kann in der Zwischenzeit andere Aufgaben ausführen.

Wenn JavaScript das awaitSchlüsselwort unterstützt (was es natürlich ( noch! ) Nicht tut ), können 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 danach genommen awaitund in einen Rückruf verkabelt, sodass sich das zweite Beispiel zur Laufzeit genauso verhält wie das erste.

Es kann nicht scheinen , wie es Ihnen viel Arbeit zu speichern, aber wenn es um Dinge wie die Ausnahmebehandlung und Synchronisation Kontexten kommt, wird der Compiler tatsächlich eine tun viel schweres Heben für Sie. Für mehr würde ich die FAQs empfehlen, gefolgt von Stephen Clearys Blogserie .

Todd Menier
quelle
Wenn Sie an dieser gefälschten Analogie festhalten (die mir übrigens sehr geholfen hat, also danke!), Was meinen Sie mit "alles danach"? Alles nur im Rahmen derselben Funktion (Methode)? Oder alles danach wie alles, was dem Aufrufstapel hätte hinzugefügt werden können?
2
"Alles nach" = der Rest der Methode. Der Compiler schreibt den Rest der Methode effektiv als Rückruf neu und die Steuerung kehrt sofort zum Aufrufer der aktuellen Methode zurück.
Todd Menier
1
Genialer Todd, nochmals vielen Dank für Ihre Erklärung. Ich bin mir sicher, dass es auch für andere nützlich ist.
-2

Wenn ich es in Java implementieren müsste, würde es ungefähr so ​​aussehen:

/**
 * @author Ilya Gazman
 */
public abstract class SynchronizedTask{

    private ArrayList<Runnable> listeners = new ArrayList<Runnable>();

    private static final ThreadPoolExecutor threadPoolExecutor =  new ThreadPoolExecutor(6, 6, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1000));

    public final void await(Runnable listener){
        synchronized (this) {
            listeners.add(listener);
        }
    }

    public void excecute(){
        onExcecute();
        for (int i = listeners.size() - 1; i >= 0; i--) {
            Runnable runnable;
            synchronized (this) {
                runnable = listeners.remove(i);
            }
            threadPoolExecutor.execute(runnable);
        }
    }

    protected abstract void onExcecute();
}

Ihre Anwendung würde es folgendermaßen verwenden:

public class Test{
    private Job job = new Job();

    public Test() {
        craeteSomeJobToRunInBackground();
        methode1();
        methode2();
    }

    private void methode1(){
        System.out.println("Running methode 1");
        job.await(new Runnable() {

            @Override
            public void run() {
                System.out.println("Continue to running methode 1");
            }
        });
    }

    private void methode2(){
        System.out.println("Running methode 2");
    }

    private void craeteSomeJobToRunInBackground() {
        new Thread(new Runnable() {

            @Override
            public void run() {
                job.excecute();
            }
        }).start();
    }

    private class Job extends SynchronizedTask{

        @Override
        protected void onExcecute() {
            try {
                Thread.sleep(1000);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Job is done");
        }
    }
}
Ilya Gazman
quelle