Exemplarische Vorgehensweise: Verwenden des Debuggers mit Async-Methoden

Mithilfe der Async-Funktion können Sie asynchrone Methoden aufrufen, ohne Rückrufe verwenden oder den Code über mehrere Methoden oder Lambda-Ausdrücke teilen zu müssen. Um synchronen Code asynchron auszuführen, rufen Sie eine asynchrone Methode anstelle einer synchronen Methode auf und fügen dem Code einige Schlüsselwörter hinzu. Weitere Informationen finden Sie unter Asynchrone Programmierung mit Async und Await (C# und Visual Basic).

Im Visual Studio-Debugger können Sie die Befehle Einzelschritt, Prozedurschritt und Ausführen bis Rücksprung mit der Async-Funktion verwenden. Sie können auch weiterhin Haltepunkte verwenden, besonders um die Ablaufsteuerung bei einer Anweisung anzuzeigen, die einen await-Operator enthält. In dieser exemplarischen Vorgehensweise führen Sie folgende Aufgaben aus, die in einer beliebigen Reihenfolge ausgeführt werden können.

  • Veranschaulichen Sie die Ablaufsteuerung an einer await-Anweisung mithilfe von Haltepunkten.

  • Erklären Sie sich das Verhalten der Befehle Einzelschritt und Prozedurschritt bei den Anweisungen, die einen await-Operator enthalten.

  • Erklären Sie sich das Verhalten des Befehls Ausführen bis Rücksprung, wenn Sie ihn in einer asynchronen Methode verwenden.

Haltepunkte zum Anzeigen der Ablaufsteuerung

Wenn Sie eine Methode mit dem Asynch-Modifizierer (Visual Basic) oder asynch-Modifizierer (C#) kennzeichnen, können Sie den Await-Operator (Visual Basic) oder await-Operator (C#) in der Methode verwenden. Um einen await-Ausdruck zu erstellen, weisen Sie dem await-Operator eine Aufgabe zu. Wenn ein await-Ausdruck für die Aufgabe aufgerufen wird, wird die aktuelle Methode sofort beendet, und es wird eine andere Aufgabe zurückgegeben. Wenn die dem await-Operator zugeordnete Aufgabe beendet wird, wird die Ausführung in derselben Methode fortgesetzt. Weitere Informationen finden Sie unter Ablaufsteuerung in asynchronen Programmen (C# und Visual Basic).

Hinweis

Eine asynchrone Methode wird an den Aufrufer zurückgegeben, wenn sie entweder auf das erste await-Objekt trifft, das noch nicht abgeschlossen wurde, oder das Ende der asynchronen Methode erreicht.

Hinweis

Die Konsolenanwendungen in diesen Beispielen verwenden die Wait-Methode, um zu verhindern, dass die Anwendung in der Main-Methode beendet wird.Sie sollten die Wait-Methode außerhalb der Konsolenanwendungen nicht verwenden, da eine Deadlocksituation auftreten kann.

Mit dem folgenden Verfahren werden Haltepunkte festgelegt, um den Ablauf zu veranschaulichen, wenn die Anwendung eine await-Anweisung erreicht. Sie können die Ablaufsteuerung auch darstellen, indem Sie Debug.WriteLine-Anweisungen hinzufügen.

  1. Erstellen Sie eine Konsolenanwendung, und fügen Sie den folgenden Code ein:

    ' Breakpoints to show control flow. 
    Imports System.Threading.Tasks
    
    Module Module1
        Sub Main()
            Dim theTask = ProcessAsync()
            Dim x = 0 ' set breakpoint
            theTask.Wait()
        End Sub
    
        Async Function ProcessAsync() As Task
            Dim result = Await DoSomethingAsync()  ' set breakpoint
    
            Dim y = 0 ' set breakpoint
        End Function
    
        Async Function DoSomethingAsync() As Task(Of Integer)
            Await Task.Delay(1000)
            Return 5
        End Function 
    End Module
    
    // Breakpoints to show control flow. 
    using System.Threading.Tasks;
    
    class Program
    {
        static void Main(string[] args)
        {
            Task theTask = ProcessAsync();
            int x = 0;  // set breakpoint
            theTask.Wait();
        }
    
        static async Task ProcessAsync()
        {
            var result = await DoSomethingAsync();  // set breakpoint 
    
            int y = 0;  // set breakpoint
        }
    
        static async Task<int> DoSomethingAsync()
        {
            await Task.Delay(1000);
            return 5;
        }
    }
    
  2. Legen Sie Debughaltepunkte auf den drei Zeilen fest, die mit dem Kommentar "Haltepunkt festlegen" enden.

  3. Drücken Sie die F5-TASTE, oder klicken Sie auf der Menüleiste auf Debuggen, Debuggen starten, um die Anwendung auszuführen.

    Die Anwendung wechselt in die ProcessAsync-Methode und hält in der Zeile mit dem await-Operator an.

  4. Drücken Sie die F5-TASTE erneut.

    Da die Anwendung an einer Anweisung mit einem await-Operator angehalten wurde, wird die Asynch-Methode sofort beendet und eine Aufgabe zurückgegeben. Daher beendet die Anwendung die ProcessAsync-Methode und hält in der aufrufenden Methode (Main) am Haltepunkt an.

  5. Drücken Sie die F5-TASTE erneut.

    Wenn die DoSomethingAsync-Methode abgeschlossen wurde, wird der Code nach der await-Anweisung in der aufrufenden Methode fortgesetzt. Folglich wird die Anwendung in der ProcessAsync-Methode am Haltepunkt unterbrochen.

    Während zunächst auf die DoSomethingAsync-Methode gewartet wurde, wurde die ProcessAsync-Methode beendet, die dann eine Aufgabe zurückgegeben hat. Als die erwartete DoSomethingAsync-Methode abgeschlossen war, wurde durch die Auswertung der await-Anweisung der Rückgabewert von DoSomethingAsync generiert. Die DoSomethingAsync-Methode ist so definiert, dass sie in Visual Basic einen Task (Of Integer)-Code oder in C# einen Task<int>-Code zurückgibt, sodass der Wert in der return-Anweisung eine ganze Zahl ist. Weitere Informationen finden Sie unter Asynchrone Rückgabetypen (C# und Visual Basic).

Abrufen und Abwarten einer Aufgabe

In der ProcessAsync-Methode ist die Dim result = Await DoSomethingAsync()-Anweisung (Visual Basic) bzw. die var result = await DoSomethingAsync();-Anweisung (C#) ein Zusammenschluss der folgenden zwei Anweisungen:

Dim theTask = DoSomethingAsync()
Dim result = Await theTask
var theTask = DoSomethingAsync();
var result = await theTask;

Die erste Codezeile ruft die Asynch-Methode auf und gibt eine Aufgabe zurück. Diese Aufgabe wird in der nächsten Codezeile dem await-Operator zugewiesen. Die await-Anweisung beendet die Methode (ProcessAsync) und gibt eine andere Aufgabe zurück. Wenn die mit dem await-Operator verknüpfte Aufgabe abgeschlossen ist, wird der Code in der Methode (ProcessAsync) nach der await-Anweisung fortgesetzt.

Wenn die await-Anweisung sofort eine andere Aufgabe zurückgibt, ist diese Aufgabe das zurückgegebene Argument der Asynch-Methode mit dem await-Operator (ProcessAsync). Die Aufgabe, die von der await-Anweisung zurückgegeben wurde, enthält die Codeausführung, die nach der await-Anweisung in derselben Methode erfolgt. Aus diesem Grund unterscheidet sich diese Aufgabe von der, die mit dem await-Operator verknüpft ist.

Einzel- und Prozedurschritt

Mit dem Befehl Einzelschritt wird ein Einzelschritt in eine Methode ausgeführt, dagegen wird mit dem Befehl Prozedurschritt der Methodenaufruf ausgeführt und die Ausführung dann an der nächsten Zeile der aufrufenden Methode unterbrochen. Weitere Informationen finden Sie unter Code schrittweise ausführen, überspringen oder bis zum Rücksprung ausführen.

Die folgende Prozedur veranschaulicht, was geschieht, wenn der Befehl Einzelschritt oder Prozedurschritt bei einer await-Anweisung ausgewählt wird.

  1. Ersetzen Sie den Code in der Konsolenanwendung durch den folgenden Code:

    ' Step Into and Step Over Example 
    Imports System.Threading.Tasks
    
    Module Module1
        Sub Main()
            ProcessAsync.Wait()
        End Sub
    
        Async Function ProcessAsync() As Task
            Dim result = Await DoSomethingAsync()  ' Step Into or Step Over from here
    
            Dim y = 0
        End Function
    
        Async Function DoSomethingAsync() As Task(Of Integer)
            Await Task.Delay(1000)
            Return 5
        End Function 
    End Module
    
    // Step Into and Step Over Example. 
    using System.Threading.Tasks;
    
    class Program
    {
        static void Main(string[] args)
        {
            ProcessAsync().Wait();
        }
    
        static async Task ProcessAsync()
        {
            var result = await DoSomethingAsync();  // Step Into or Step Over from here 
    
            int y = 0;
        }
    
        static async Task<int> DoSomethingAsync()
        {
            await Task.Delay(1000);
            return 5;
        }
    }
    
  2. Drücken Sie die F11-TASTE, oder wählen Sie in der Menüleiste Debuggen, Einzelschritt aus, um eine Demonstration des Step Into-Befehls bei einer Anweisung mit einem await-Operator zu starten.

    Die Anwendung wird gestartet und an der ersten Zeile unterbrochen, wobei es sich in Visual Basic um die Sub Main()- Methode oder in C# um die öffnende geschweifte Klammer der Main-Methode handelt.

  3. Drücken Sie die F11-TASTE drei Mal.

    Die Anwendung sollte sich nun an der await-Anweisung in der ProcessAsync-Methode befinden.

  4. Drücken Sie die Taste F11.

    Die Anwendung wechselt in die DoSomethingAsync-Methode und wird an der ersten Zeile unterbrochen. Dieses Verhalten tritt auch dann bei der await-Anweisung auf, wenn die Anwendung sofort zur aufrufenden Methode (Main) zurückkehrt.

  5. Halten Sie die F11-TASTE so lange gedrückt, bis die Anwendung zur await-Anweisung in der ProcessAsync-Methode zurückkehrt.

  6. Drücken Sie die Taste F11.

    Die Anwendung wird in der Zeile unterbrochen, die der await-Anweisung folgt.

  7. Wählen Sie in der Menüleiste Debuggen, Debuggen beenden aus, um die Ausführung der Anwendung zu beenden.

    In den nächsten Schritten wird der Befehl Prozedurschritt bei einer await-Anweisung veranschaulicht.

  8. Drücken Sie die F11-TASTE vier Mal, oder wählen Sie auf der Menüleiste Debuggen, Einzelschritt aus.

    Die Anwendung sollte sich nun an der await-Anweisung in der ProcessAsync-Methode befinden.

  9. Drücken Sie die F10-TASTE, oder wählen Sie in der Menüleiste Debuggen, Prozedurschritt aus.

    Die Ausführung wird in der Zeile unterbrochen, die der await-Anweisung folgt. Dieses Verhalten tritt auch dann bei der await-Anweisung auf, wenn die Anwendung sofort zur aufrufenden Methode (Main) zurückkehrt. Der Step Over-Befehl überspringt auch erwartungsgemäß die Ausführung der DoSomethingAsync-Methode.

  10. Wählen Sie in der Menüleiste Debuggen, Debuggen beenden aus, um die Ausführung der Anwendung zu beenden.

    Wie die folgenden Schritte verdeutlichen, unterscheidet sich das Verhalten der Befehle Einzelschritt und Prozedurschritt geringfügig, wenn sich der await-Operator in einer anderen Zeile als der Aufruf der Asynch-Methode befindet.

  11. Ersetzen Sie die await-Anweisung in der ProcessAsync-Methode durch den folgenden Code. Die ursprüngliche await-Anweisung ist eine Zusammenfassung der folgenden zwei Anweisungen.

    Dim theTask = DoSomethingAsync()
    Dim result = Await theTask
    
    var theTask = DoSomethingAsync();
    var result = await theTask;
    
  12. Drücken Sie die F11-TASTE, oder wählen Sie in der Menüleiste Debuggen, Einzelschritt mehrmals aus, um die Ausführung zu starten und den Code zu durchlaufen.

    Beim Aufruf der DoSomethingAsync-Methode wird der Befehl Einzelschritt in der DoSomethingAsync-Methode unterbrochen, während der Befehl Prozedurschritt erwartungsgemäß zur nächsten Anweisung wechselt. Bei der await-Anweisung werden beide Befehle an der Anweisung unterbrochen, die der await-Anweisung folgt.

Ausführen bis Rücksprung

Bei nicht asynchronen Methoden wird in der aufrufenden Methode durch den Befehl Ausführen bis Rücksprung der Code unterbrochen, der dem Methodenaufruf folgt. Bei asynchronen Methoden ist die Logik für die Stelle, an der die Ausführung in der aufrufenden Methode unterbrochen wird, komplexer. Diese Logik hängt davon ab, ob sich der Befehl Ausführen bis Rücksprung in der ersten Zeile der Asynch-Methode befindet.

  1. Ersetzen Sie den Code in der Konsolenanwendung durch den folgenden Code:

    ' Step Out Example 
    Imports System.Threading.Tasks
    
    Module Module1
        Sub Main()
            ProcessAsync.Wait()
        End Sub
    
        Async Function ProcessAsync() As Task
            Dim theTask = DoSomethingAsync()
            Dim z = 0
            Dim result = Await theTask
        End Function
    
        Async Function DoSomethingAsync() As Task(Of Integer)
            Debug.WriteLine("before")  ' Step Out from here
            Await Task.Delay(1000)
            Debug.WriteLine("after")
            Return 5
        End Function 
    End Module
    
    // Step Out Example. 
    using System.Diagnostics;
    using System.Threading.Tasks;
    
    class Program
    {
        static void Main(string[] args)
        {
            ProcessAsync().Wait();
        }
    
        static async Task ProcessAsync()
        {
            var theTask = DoSomethingAsync();
            int z = 0;
            var result = await theTask;
        }
    
        static async Task<int> DoSomethingAsync()
        {
            Debug.WriteLine("before");  // Step Out from here
            await Task.Delay(1000);
            Debug.WriteLine("after");
            return 5;
        }
    }
    
  2. Legen Sie in der Debug.WriteLine("before")-Zeile in der DoSomethingAsync-Methode einen Haltepunkt fest.

    Hiermit wird das Verhalten des Befehls Ausführen bis Rücksprung von einer Zeile in einer Asynch-Methode veranschaulicht, bei der es sich nicht um die erste Zeile handelt.

  3. Drücken Sie die F5-TASTE, oder klicken Sie auf der Menüleiste auf Debuggen, Debuggen starten, um die Anwendung zu starten.

    Der Code wird in der Debug.WriteLine("before")-Zeile in der DoSomethingAsync-Methode unterbrochen.

  4. Drücken Sie UMSCHALT+F11, oder wählen Sie in der Menüleiste Debuggen, Ausführen bis Rücksprung aus.

    Die Anwendung wird in der aufrufenden Methode an der await-Anweisung für die Aufgabe unterbrochen, die mit der aktuellen Methode verknüpft ist. Daher wird die Anwendung in der ProcessAsync-Methode an der await-Anweisung unterbrochen. Die Anwendung wird in Visual Basic nicht am Dim z = 0-Code bzw. in C# nicht am int z = 0;-Code unterbrochen. Hierbei handelt es sich um den Code, der dem Aufruf der DoSomethingAsync-Methode folgt.

  5. Wählen Sie in der Menüleiste Debuggen, Debuggen beenden aus, um die Ausführung der Anwendung zu beenden.

    Die nächsten Schritte zeigen, was geschieht, wenn Sie in der ersten Zeile einer Asynch-Methode den Befehl Ausführen bis Rücksprung durchführen.

  6. Entfernen Sie den vorhandenen Haltepunkt, und fügen Sie in der erste Zeile der DoSomethingAsync-Methode einen Haltepunkt hinzu.

    Fügen Sie in C# den Haltepunkt zur öffnenden geschweiften Klammer der DoSomethingAsync-Methode hinzu. Fügen Sie in Visual Basic den Haltepunkt in der Zeile mit Async Function DoSomethingAsync() As Task(Of Integer) hinzu.

  7. Drücken Sie die F5-TASTE, um die Anwendung zu starten.

    Der Code wird in der ersten Zeile der DoSomethingAsync-Methode unterbrochen.

  8. Klicken Sie in der Menüleiste auf Debuggen, Fenster und Ausgabe.

    Das Ausgabefenster wird geöffnet.

  9. Drücken Sie UMSCHALT+F11, oder wählen Sie in der Menüleiste Debuggen, Ausführen bis Rücksprung aus.

    Die Anwendung wird fortgesetzt, bis die Asynch-Methode die erste await-Anweisung erreicht. Die Anwendung wird dann an der aufrufenden Anweisung unterbrochen. Daher wird die Anwendung bei der Dim the Task = DoSomethingAsync()-Methode (Visual Basic) bzw. bei der var theTask = DoSomethingAsync();-Methode (C#) unterbrochen. Die "Vorher"-Meldung wird im Ausgabefenster angezeigt, die "Nachher"-Meldung jedoch noch nicht.

  10. Drücken Sie die F5-TASTE, um die Ausführung der Anwendung fortzusetzen.

    Die Anwendung wird mit der Anweisung fortgesetzt, die der await-Anweisung in der aufgerufenen Asynch-Funktion (DoSomethingAsync) folgt. Im Ausgabefenster wird die "Nachher"-Meldung angezeigt.

Siehe auch

Konzepte

Starten, Unterbrechen, schrittweise Ausführung, Ausführen von Code und Beenden des Debuggens in Visual Studio