译如何在 Visual Studio 中调试异步代码
Posted MeteorSeed
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了译如何在 Visual Studio 中调试异步代码相关的知识,希望对你有一定的参考价值。
虽然异步代码可以提高程序的整体吞吐量,但异步代码仍然无法免除错误!当潜在的死锁、模糊的错误消息以及查找导致 Bug 的 Task 时,编写异步代码会使调试更加困难。幸运的是,Visual Studio 具有与托管的、本地的和 JavaScript 兼容的多个新旧功能,可帮助缓解调试异步代码的挫折感。下面,让我们开始吧。
在哪里可以看到程序的所有Task
当你在异步编程时遇到 bug,你可能想看下你所有的 Task,然后确定到底是哪出错了。如果你调试过多线程应用,你可能会对“Threads”窗口比较熟悉。好消息,对于 Task 来说也是等效的窗口。“Tasks”窗口允许你产看所有任务,显示它们的 id、当前位置、最初传递给它们的方法以及它们在中断时的当前状态(active 活动的、scheduled 计划的、blocked 阻塞的或 deadlocked 死锁的)。如果你的程序是多线程的,此窗口还将显示运行每个任务的线程。这有助于识别可能导致问题的特定线程。
可以在 Debug > Windows > Task 或者使用 CTRL+SHIFT+D, K 来访问 Tasks 窗口。
如何在我的异步代码中定位异常的源头
在异步编程时,确定抛出异常的源头往往是令人沮丧的。当一个异常被多次抛出,Visual Studio 通常返回调用堆栈,其中的异常是最近通过Exception Helper 抛出的。不幸的是,这并不总是有助于异步调试。为了解决这个问题,我们在 VS16.4 版本中实现了重新抛出异常。使用此更新,Exception Helper 将在重新抛出异常时显示原始调用堆栈。
有没有办法更好地可视化任务和异步代码流?
对于异步代码执行的图形化描述,Visual Studio 具有线程和任务的并行堆栈(Parallel Stacks)窗口。对 Visual Studio 16.6 进行了改进,任务的并行堆栈窗口(或并行任务窗口(Parallel Tasks))直观地显示活动、等待和计划的任务以及它们之间的关系。双击活动任务或等待任务,将在“调用堆栈(Call Stack)”窗口中显示异步调用堆栈。若要了解哪个线程正在运行特定任务,可以在“并行线程(Parallel Threads)”和“并行任务(Parallel Tasks)”窗口之间进行交换。可以通过右键单击并在上下文菜单中选择“转到线程(Go To Thread)”来完成此操作。
准备好使用这些工具来调试异步代码了吗?
现在你有了更多的工具来帮助你调试代码,和我们分享你的反馈吧!您的想法和功能建议将帮助我们创建最好的异步调试体验。
原文链接
作者:MeteorSeed
感谢您阅读本文,如果您觉得有所收获,麻烦点一下右边的“推荐”,您的支持是对我最大的鼓励...
转载请注明出处。
异步/等待异常和 Visual Studio 2013 调试输出行为
【中文标题】异步/等待异常和 Visual Studio 2013 调试输出行为【英文标题】:Async/await exception and Visual Studio 2013 debug output behavior 【发布时间】:2014-10-03 05:50:13 【问题描述】:我正在用 C# 开发一个通过 Web 服务与 Dynamics NAV 通信的应用程序。为了减少重复代码并且因为会有很多端点,我创建了一个通用的 async/await 方法来执行服务调用并处理异常。
该方法有效,但当异常发生(并得到处理)时,我在 Visual Studio 2013 输出窗口中看到了意外行为。
测试代码和输出如下所示。
我担心的是“类型的第一次机会异常...”行,我在使用 async/await 方法时看到了 4 次。这个异常真的发生了 4 次吗?
当同步调用服务时,只有一个预期的异常行。
这只是 Visual Studio 2013 还是我的 async/await 代码有问题?
有没有更好的方法来做我想要完成的事情?
class Program
static void Main(string[] args)
Debug.WriteLine("Synchronous...");
try
TestFunctions_PortClient service = new TestFunctions_PortClient();
service.Open();
string result = service.ErrorTest();
Debug.WriteLine(result);
catch (Exception ex)
Debug.WriteLine(ex.Message);
Debug.WriteLine(string.Empty);
Debug.WriteLine("Async...");
NavServiceTest navService = new NavServiceTest();
navService.TestAsync();
Console.ReadLine();
class NavServiceTest
public async void TestAsync()
try
string result = await CallServiceAsync();
Debug.WriteLine(result);
catch (Exception ex)
Debug.WriteLine(ex.Message);
private async Task<string> CallServiceAsync()
TestFunctions_PortClient service = new TestFunctions_PortClient();
service.Open();
ErrorTest_Result result = await ExecuteServiceAsync<ErrorTest_Result>(
service.InnerChannel,
service.Endpoint,
service.ErrorTestAsync());
return result.return_value;
private async Task<T> ExecuteServiceAsync<T>(IClientChannel channel, ServiceEndpoint endpoint, Task<T> source)
var tcs = new TaskCompletionSource<T>();
Task<T> task = tcs.Task;
try
Debug.WriteLine("ExecuteServiceAsync");
tcs.TrySetResult(await source);
catch (EndpointNotFoundException ex)
Debug.WriteLine("EndpointNotFoundException");
tcs.TrySetException(ex);
catch (FaultException ex)
Debug.WriteLine("FaultException");
tcs.TrySetException(ex);
catch (Exception ex)
Debug.WriteLine("Exception");
tcs.TrySetException(ex);
finally
if (channel != null)
if (channel.State == CommunicationState.Faulted)
channel.Abort();
else
channel.Close();
if (task.IsFaulted)
throw task.Exception.InnerException;
return task.Result;
这是上面代码的输出。
Synchronous...
A first chance exception of type 'System.ServiceModel.FaultException' occurred in mscorlib.dll
Error from NAV
Async...
ExecuteServiceAsync
A first chance exception of type 'System.ServiceModel.FaultException' occurred in mscorlib.dll
FaultException
A first chance exception of type 'System.ServiceModel.FaultException' occurred in ServiceTest.exe
A first chance exception of type 'System.ServiceModel.FaultException' occurred in mscorlib.dll
A first chance exception of type 'System.ServiceModel.FaultException' occurred in mscorlib.dll
Error from NAV
【问题讨论】:
【参考方案1】:当异步方法中发生异常时,它不会像在同步代码中那样沿堆栈向上传播。哎呀,逻辑堆栈可能不再存在了。
相反,异常存储在代表异步操作的任务中。那么,当你await
异步操作时,TaskAwaiter
的GetResult
方法会重新抛出原来的异常。如果您的代码中没有捕获到,那么它将再次被编译器生成的代码捕获并放入代表 that 操作的任务中,等等。所以如果你有一个异步方法链(通常是这种情况)并且最深的一个抛出异常,异常传播实际上将是链中每个链接的“抛出GetResult
,捕获,填充到任务中”。
所以是的,异常是被抛出四次,为了有效只被抛出一次。如果您担心这样做的效率,我怀疑这还不错 - 因为逻辑堆栈跟踪只确定一次。我敢说它比同步版本效率低,但我的总体理念是,如果您看到如此多的异常以至于它们严重影响了您的性能,那么您要么过度使用异常,要么您的系统处于非常糟糕的状态无论如何,性能是您最不必担心的问题。
【讨论】:
嗯,性能不是问题,但这让我想知道是否有我没有发现的异常。但是,在阅读了您的答案之后,这一切现在都说得通了,另外 3 个例外也对应于我所拥有的 3 个等待,所以一切都很好。感谢乔恩的解释。以上是关于译如何在 Visual Studio 中调试异步代码的主要内容,如果未能解决你的问题,请参考以下文章
调试器在 Visual Studio 中的异步 HttpClient.GetAsync() 调用后停止
Visual Studio Code - 没有 ptvsd 的远程调试
异步/等待异常和 Visual Studio 2013 调试输出行为
JAVA优化师入门教程——如何使用visual studio 对mysql进行源码级调试和优化