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

本文关键字:调试 2013 输出 Studio Visual 等待 异常 异步 | 更新日期: 2023-09-27 18:25:22

我正在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

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

当异步方法中发生异常时,它不会像在同步代码中那样向上传播。见鬼,逻辑堆栈可能不再存在了。

相反,异常存储在代表异步操作的任务中。然后,当await异步操作时,TaskAwaiterGetResult方法将重新引发原始异常。如果它没有被捕获到代码中,那么它将再次被编译器生成的代码捕获,并被放入表示操作的任务中,等等。因此,如果你有一个异步方法链(通常是这样),并且最深的方法抛出异常,那么异常传播实际上将是链中每个链接的"抛出GetResult,catch,填充到任务中"。

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