Асинхронное исключение / ожидание и поведение отладочного вывода Visual Studio 2013

1

Я разрабатываю приложение на С#, которое взаимодействует с Dynamics NAV через веб-службы. Чтобы уменьшить дублирующийся код и потому, что будет много конечных точек, я создал общий метод async/await, который выполняет вызовы служб и обрабатывает исключения.

Этот метод работает, но я вижу неожиданное поведение в окне вывода Visual Studio 2013, когда возникает исключение (и обрабатывается).

Тестовый код и вывод можно увидеть ниже.

Моя забота - это строки "Первое случайное исключение типа...", которое я вижу 4 раза при использовании методов async/wait. Действительно ли это исключение происходит 4 раза?

При вызове службы синхронно существует только одна строка исключений, которая ожидается.

Это просто Visual Studio 2013 или что-то не так с моим кодом async/wait?

Может быть, лучший способ сделать то, что я пытаюсь выполнить?

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
Теги:
async-await
web-services
visual-studio-debugging

1 ответ

10
Лучший ответ

Когда исключение возникает в асинхронном методе, оно не просто распространяется по стеку, как в синхронном коде. Heck, логический стек, вероятно, больше не будет.

Вместо этого исключение сохраняется в задаче, которая представляет асинхронную операцию. Затем, когда вы await асинхронную операцию, то GetResult метод TaskAwaiter будет повторно выдать исходное исключение. Если это не попало в ваш код, он снова будет схвачен сгенерированным компилятором кодом и помещен в задачу, которая представляет эту операцию, и т.д. Поэтому, если у вас есть цепочка асинхронных методов (как это часто бывает) и самый глубокий из них генерирует исключение, распространение исключений на самом деле будет "бросить GetResult, уловить, вещи в задачу" на ссылку в цепочке.

Так что да, исключение бросается четыре раза, чтобы эффективно только бросать один раз. Если вы беспокоитесь об эффективности этого, я подозреваю, что это не так уж плохо - потому что логическая трассировка стека определяется только один раз. Я смею сказать, что это менее эффективно, чем синхронная версия, но моя общая философия заключается в том, что если вы видите так много исключений, что они существенно влияют на вашу производительность, то либо вы злоупотребляете исключениями, либо ваша система находится в очень плохом состоянии во всяком случае, и производительность - это наименьшее из ваших забот.

  • 0
    Ну, производительность не является проблемой, но меня удивило, были ли исключения, которые я не уловил. Но все это имеет смысл сейчас после прочтения вашего ответа, и 3 дополнительных исключения также соответствуют 3 ожиданиям, которые у меня есть, так что все хорошо. Спасибо за объяснение, Джон.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню