c# - 异步方法中的奇怪调试器行为

标签 c# debugging asynchronous return throw

当我越过代码中的断点时,我遇到了调试器的奇怪行为:

public async Task DoSomeWork()
{
     await Task.Run(() => { Thread.Sleep(1000); });

     var test = false;
     if (test)
     {
          throw new Exception("Im in IF body!");
     }
}

调试器进入 if 主体。值得注意的是,异常并没有真正被抛出,而只是看起来像被抛出。因此,如果您将断点放在 throw 上,则无法重现。您必须将它放在上面,然后下到 if 主体才能捕获它。这同样适用于任何类型的异常实例(以及显式 null),甚至适用于 return 而不是 throw

除此之外,即使我删除带有 await 的行,它也能正常工作。

我尝试从不同的 PC 运行此代码片段,因此它不是 PC 的问题。我还认为这是 VS 代码中的错误,并尝试在 JetBrains 的 Rider 中运行它 - 结果相同。

我确定这是异步的,但它是如何明确工作的?

最佳答案

您的代码使用 Visual Studio 2015 在 “调试” 构建中轻松重现了该问题。我只需要添加 Program.Main(),使用调用 DoSomeWork().Wait();,在方法中设置断点并单步执行。

至于为什么会这样,这无疑是重写async方法和生成调试数据库(.pdb)的结合。与迭代器方法类似,向方法添加 async 会导致编译器将您的方法更改为状态机。生成的实际 IL 看起来与原始方法只有一点点相似。也就是说,如果您查看它,您可以识别原始代码的关键组件,但它现在位于一个大的 switch 语句中,该语句处理方法在每个 await< 返回时发生的事情 语句,然后在每个等待的表达式完成时重新输入。

当程序语句看似在throw 上时,实际上是在方法中隐式的return 语句上。只是可执行文件的调试数据库没有为该行提供程序语句。

有一个提示,在调试时,这就是正在发生的事情。当您越过 if 语句时,您会注意到它直接进入了 throw 语句。如果 if 语句 block 真的被输入,下一个程序语句行实际上是该 block 的左大括号,而不是程序语句。

您还可以添加例如方法末尾的 Console.WriteLine(),这将为调试器提供足够的信息来进行同步,而不会显示错误的行号。

有关编译器如何处理 async 方法的更多信息,请参阅 Is the new C# async feature implemented strictly in the compiler ,以及那里提供的链接(包括乔恩关于该主题的系列文章)。

关于c# - 异步方法中的奇怪调试器行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50876080/

相关文章:

c# - FluentNhibernate + 私有(private)集

c# - 如何在列表框中显示查询结果?

node.js - 调试proctor(带/不带webstorm)

jquery - 我的代码有问题吗?

ios 基于 key 创建写锁

java - 使用 CompletableFuture 从主线程退出

c# - 通过外键获取图像

c# - 'XmlDocument.CreateElement();' 只创建一个元素吗?

mysql - 如何确定 Mono 2.10.2 中缺少哪个程序集?

c# - andcontinue() - 作为异步操作执行的方法