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

Strange debugger behaviour in async method(异步方法中奇怪的调试器行为)

本文介绍了异步方法中奇怪的调试器行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

When I stepped over breakpoints in my code I have encountered strange behaviour of debugger:

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.

Debugger goes into if body. It's remarkable that the exception is not really thrown but just looks like it is. So you can't reproduce that if you place breakpoint right on throw. You must place it above and step down to the if body to catch it. The same works on any kind of exception instance (as well as explicit null) and even on return instead of throw.

此外,即使我使用 await 删除行,它也可以工作.

Besides that it works even if I remove line with await.

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

I tried to run this code snippet from different PCs so its not a PC trouble. Also I have thought it is bug in VS code and tried to run it in Rider from JetBrains - the same result.

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

I'm sure it's the async thing but how it explicitly works?

推荐答案

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

Your code reproduces the issue easily, in a "Debug" build, using Visual Studio 2015. I had only to add in Program.Main(), with a call to DoSomeWork().Wait();, set a breakpoint in the method and step through it.

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

As for why it happens, this is undoubtedly due to the combination of the async method being rewritten and the debugging database (.pdb) generated. Similar to iterator methods, adding async to the method causes the compiler to change your method into a state machine. The actual IL that's generated looks only a little bit like the original method. That is, if you look at it, you can identify the key components of the original code, but it's now in a big switch statement that handles what happens as the method returns at each await statement, and then is re-entered with the completion of each awaited expression.

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

When the program statement appears to be on the throw, it's really at the implicit return statement in the method. It's just that the debugging database for the executable doesn't provide a program statement for that line.

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

There's a hint, when debugging, that that's what's happening. When you step over the if statement, you'll notice it goes straight to the throw statement. If the if statement block were really being entered, the next program statement line would actually be the opening brace for the block, not the program statement.

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

You can also add e.g. a Console.WriteLine() at the end of the method, and that will give the debugger enough information to sync up and not show you at the wrong line number.

有关编译器如何处理 async 方法的更多信息,请参阅新的 C# 异步功能是否严格实现在编译器中,以及那里提供的链接(包括 Jon 关于该主题的系列文章).

For additional information on how async methods are handled by the compiler, see Is the new C# async feature implemented strictly in the compiler, and the links provided there (including Jon's series of articles on the topic).

这篇关于异步方法中奇怪的调试器行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:异步方法中奇怪的调试器行为

基础教程推荐