异步/等待异常和 Visual Studio 2013 调试输出行为

Posted

技术标签:

【中文标题】异步/等待异常和 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异步操作时,TaskAwaiterGetResult方法会重新抛出原来的异常。如果您的代码中没有捕获到,那么它将再次被编译器生成的代码捕获并放入代表 that 操作的任务中,等等。所以如果你有一个异步方法链(通常是这种情况)并且最深的一个抛出异常,异常传播实际上将是链中每个链接的“抛出GetResult,捕获,填充到任务中”。

所以是的,异常被抛出四次,为了有效只被抛出一次。如果您担心这样做的效率,我怀疑这还不错 - 因为逻辑堆栈跟踪只确定一次。我敢说它比同步版本效率低,但我的总体理念是,如果您看到如此多的异常以至于它们严重影响了您的性能,那么您要么过度使用异常,要么您的系统处于非常糟糕的状态无论如何,性能是您最不必担心的问题。

【讨论】:

嗯,性能不是问题,但这让我想知道是否有我没有发现的异常。但是,在阅读了您的答案之后,这一切现在都说得通了,另外 3 个例外也对应于我所拥有的 3 个等待,所以一切都很好。感谢乔恩的解释。

以上是关于异步/等待异常和 Visual Studio 2013 调试输出行为的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio Code Python 调试“发生异常 SystemExit”

未能正确创建Visual c# 20xx,因此未能打开工程xxx,请重新安装Visual studio

从 TFS 仪表板打开项目导致 Visual Studio Web 处理程序异常

visual studio怎么安装

visual studio 2010自定义类继承CString问题

译如何在 Visual Studio 中调试异步代码