演练:将调试器与异步方法一起使用

使用异步功能,可以调用异步方法,而不使用回调或拆分您在多个方法或 lambda 表达式中的代码。 若要使同步代码异步,则调用异步方法而不是一个同步方法并添加几个关键字到代码中。 有关更多信息,请参见使用 Async 和 Await 的异步编程(C# 和 Visual Basic)

在 Visual Studio 调试器,可以使用 单步执行逐过程跳出 命令与 Async 功能。 还可以继续使用断点,尤其是查看包含一个等待运算符的语句的控制流。 在本演练中,您将完成以下任务,您可以按任意顺序运行。

  • 演示在等待语句的控制流使用断点。

  • 了解 单步执行逐过程 命令的行为在包含一等待运算符的语句。

  • 当将它从异步方法内时,了解 跳出 命令的行为。

显示控制流的断点

如果标记与 异步 (Visual Basic) 或 (c#) async 修饰符的方法,在方法可以使用 Await (Visual Basic) 或 (c#) 等待 运算符。 若要创建等待表达式,可以将等待运算符与任务。 当等待表达式为任务调用时,当前方法立即退出并返回一个不同的任务。 在与等待运算符时的任务完成,执行在同一方法还原。 有关更多信息,请参见异步程序中的控制流(C# 和 Visual Basic)

备注

异步方法回调用方,当数组中遇到不是完成时或到达异步方法的末尾的第一个等待的对象,后者首先进行。

备注

在这些示例中的控件个 apps 使用 Wait 方法将阻止应用程序停止。Main。因为死锁情况发生,您不应使用 Wait 方法在控件个应用程序之外。

以下程序集断点演示发生的情况,当应用程序到达等待语句。 还可以演示控制流通过添加 Debug.WriteLine 语句。

  1. 控件创建一个应用程序,然后粘贴以下代码:

    ' 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. 将以“设置的断点”注释结尾的三行的调试断点。

  3. 选择 F5 键或选择 调试,在菜单栏上 启动调试 运行应用程序。

    应用程序在包含等待运算符的行输入 ProcessAsync 方法并中断。

  4. 再次选择 F5 键。

    由于中包含一个等待运算符的语句终止的应用程序,应用程序会立即退出异步方法并返回任务。 因此,应用程序退出 ProcessAsync 方法并中断在调用方法 (Main) 的断点。

  5. 再次选择 F5 键。

    当 DoSomethingAsync 方法完成时,代码在调用方法的时间语句后继续。 因此,应用程序中断在 ProcessAsync 方法中的断点。

    当 DoSomethingAsync 最初等待,ProcessAsync 方法退出并返回任务。 当等待的 DoSomethingAsync 方法来完成后,等待语句的计算产生了 DoSomethingAsync的返回值。 DoSomethingAsync 方法定义返回在 Visual Basic 中 C# 中 Task (Of Integer)Task<int>,因此,在其值返回语句是整数。 有关更多信息,请参见异步返回类型(C# 和 Visual Basic)

JJ155813.collapse_all(zh-cn,VS.110).gif获取然后等待任务

在 ProcessAsync 方法,语句 Dim result = Await DoSomethingAsync() (Visual Basic) 或 var result = await DoSomethingAsync(); (c#) 是以下两个语句的收缩:

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

第一行代码调用异步方法并返回任务。 该任务与下一行代码的时间运算符。 等待语句退出方法 (ProcessAsync) 并返回其他任务。 在与等待运算符时的任务已完成,代码在方法 (ProcessAsync) 以等待语句之后。

当等待语句立即返回其他任务时,该任务是包含等待运算符异步方法的返回参数 (ProcessAsync)。 由等待返回的任务包括发生,在同一方法后的时间,是的代码执行该任务的原因是使用与等待的任务不同。

单步执行并单步执行

单步执行 命令单步执行方法,但是,逐过程 命令在调用方法的下一行执行方法调用然后中断。 有关更多信息,请参见[NIB] 代码单步执行概述

下面的过程演示发生,当选择 单步执行逐过程 命令在等待语句。

  1. 用下面的代码替换该控件个应用程序的代码。

    ' 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. 选择 F11 键或选择 调试,在菜单栏上 单步执行 开始 Step Into 命令的演示中包含一个等待运算符的语句。

    应用程序在第一行开始并中断,是 Visual Basic 或 Main 方法左大括号后面的 Sub Main() 在 C# 中。

  3. 再选择 F11 键三倍。

    应用程序现在应在 ProcessAsync 方法的时间语句。

  4. 选择 F11 键。

    应用程序在第一行输入 DoSomethingAsync 方法并中断。 发生此行为,即使,在 await 语句,应用程序会立即回调用方法 (Main)。

  5. 选择直到应用程序的 Keep F11 键返回到 ProcessAsync 方法的时间语句。

  6. 选择 F11 键。

    应用程序在后面等待语句的换行符。

  7. 在菜单栏上,依次选择 调试停止调试 停止应用程序的执行。

    以下步骤演示 逐过程 命令在等待语句。

  8. 选择 F11 键四次或选择 调试,在菜单栏上 单步执行 四次。

    应用程序现在应在 ProcessAsync 方法的时间语句。

  9. 选择 F10 键或选择 调试,在菜单栏上 逐过程

    中断执行在后面等待语句的行。 发生此行为,即使应用程序会立即回调用方法 (Main) 在等待语句。 Step Over 命令进行单步执行 DoSomethingAsync 方法的执行,按预期方式工作。

  10. 在菜单栏上,依次选择 调试停止调试 停止应用程序的执行。

    在下面的步骤演示,单步执行逐过程 命令的行为稍有不同,当等待运算符是在从调用不同的行添加到异步方法时。

  11. 在 ProcessAsync 方法,用以下代码替换等待语句。 原始的时间语句是以下两个语句的缩小。

    Dim theTask = DoSomethingAsync()
    Dim result = Await theTask
    
    var theTask = DoSomethingAsync();
    var result = await theTask;
    
  12. 选择 F11 键或选择 调试,在菜单栏多个纪元的 单步执行 通过代码开始执行和步骤。

    在对 DoSomethingAsync的调用,单步执行 命令在 DoSomethingAsync 方法,中断,而 逐过程 命令转至下一语句,按预期方式工作。 在等待语句,两个命令中断在后面等待的语句。

跳出

在不是异步的方法,跳出 命令在下一个方法调用代码中调用的方法中断。 对于异步方法,执行在调用方法中断的逻辑是较为复杂和该逻辑取决于 跳出 命令是否可以在异步方法的第一行。

  1. 用下面的代码替换该控件个应用程序的代码。

    ' 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. 将 Debug.WriteLine("before") 行中设置断点在 DoSomethingAsync 方法。

    这是为了演示 跳出 命令的行为从一行中不是第一行的异步方法。

  3. 选择 F5 键或选择 调试,在菜单栏上 启动调试 启动应用程序。

    代码隐藏 DoSomethingAsync 方法的 Debug.WriteLine("before") 中断。

  4. 选择 Shift+F11 键或选择 调试,在菜单栏上 跳出

    应用程序在调用方法上中断与当前方法的任务的时间语句。 因此,应用程序在 ProcessAsync 方法的时间中断语句。 应用程序在 Dim z = 0 (Visual Basic) 或 int z = 0; (c#) 不会中断,是代码遵循调用 DoSomethingAsync 方法。

  5. 选择 调试,在菜单栏上 停止调试 停止应用程序的执行。

    以下步骤演示发生的情况,当从异步方法的第一行的 跳出

  6. 移除现有断点,并添加在 DoSomethingAsync 方法的第一行中设置断点。

    在 c# 中,添加对于 DoSomethingAsync 方法的左大括号的断点。 在 Visual Basic 中,添加对于包含 Async Function DoSomethingAsync() As Task(Of Integer)"的行中设置断点。

  7. 选择 F5 键启动应用程序。

    代码在 DoSomethingAsync 方法的第一行中断。

  8. 在菜单栏上,依次选择 调试Windows输出

    将打开**“输出”**窗口。

  9. 选择 Shift+F11 键或选择 调试,在菜单栏上 跳出

    应用程序还原,直到异步方法达到其第一个等待,应用程序然后中断在调用语句。 因此,应用程序在 Dim the Task = DoSomethingAsync() " (Visual Basic) 或 var theTask = DoSomethingAsync(); (c#) 中断。 “,”消息出现在"输出"窗口之前,但是,“,”未显示消息之后。

  10. 选择 F5 键继续运行应用程序。

    应用程序将继续按照在调用异步功能的语句 (DoSomethingAsync) 的时间语句。 “在”消息之后出现在"输出"窗口。

请参见

概念

[NIB] 代码单步执行概述