Исключение, сгенерированное в асинхронном обработчике, не перехватывается и не обрабатывается - PullRequest
0 голосов
/ 02 июня 2018

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

Он не попадает внутрь TestTestAsync (я полагаю, из-за invoke wait только самый быстрый), но почемуоно не попадает в необработанное, ненаблюдаемое или аварийное приложение?

ThrowUnobservedTaskExceptions = true также не имеет никакого смысла.

using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp5
{
    class Program
    {
        static string lockStr = Guid.NewGuid().ToString();

        public static void ConsoleWriteLine(string Message, ConsoleColor? color = null)
        {
            lock (lockStr)
            {
                var old = Console.ForegroundColor;

                if (color != null)
                    Console.ForegroundColor = color.Value;

                Console.WriteLine(Message);

                if (color != null)
                    Console.ForegroundColor = old;
            }
        }

        static void Main(string[] args)
        {
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
            TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;

            try
            {
                var cls = new TestClass();
                cls.TestAsync += async (s) => await Cls_TestRealAsyncAsync(s);
                cls.TestAsync += Cls_TestRealAsync;

                Task.Run(async () => await cls.TestTestAsync()).Wait();

                Thread.Sleep(5000);
            }
            catch (Exception ex)
            {
                ConsoleWriteLine($"{nameof(Main)}: {ex.Message}");
            }
        }

        private static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
        {
            ConsoleWriteLine($"{nameof(TaskScheduler_UnobservedTaskException)}: {(e.Exception as Exception).Message}", ConsoleColor.Yellow);
        }

        private static Task Cls_TestRealAsync(object sender)
        {
            try
            {
                Thread.Sleep(100);
                throw new NotImplementedException($"{nameof(Cls_TestRealAsync)}");
            }
            catch (Exception ex)
            {
                ConsoleWriteLine(ex.Message, ConsoleColor.Red);
                throw;
            }
        }

        private static async Task Cls_TestRealAsyncAsync(object sender)
        {
            try
            {
                await Task.Run(() => Thread.Sleep(1000));
                throw new NotImplementedException($"{nameof(Cls_TestRealAsyncAsync)}");
            }
            catch (Exception ex)
            {
                ConsoleWriteLine(ex.Message, ConsoleColor.Red);
                throw;
            }
        }

        private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            ConsoleWriteLine($"{nameof(CurrentDomain_UnhandledException)}: {(e.ExceptionObject as Exception).Message}", ConsoleColor.Yellow);
        }
    }

    public class TestClass
    {
        public delegate Task TestHandlerAsync(object sender);
        public event TestHandlerAsync TestAsync;

        private async Task OnTestAsync()
        {
            if (TestAsync != null)
                await TestAsync.Invoke(this);
        }

        public async Task TestTestAsync()
        {
            try
            {
                await OnTestAsync();
            }
            catch (Exception ex)
            {
                Program.ConsoleWriteLine($"{nameof(TestTestAsync)}: {ex.Message}", ConsoleColor.Green);
            }
        }
    }
}

Screenshot

PS: я сделал тесты на 4.7.1

Ответы [ 2 ]

0 голосов
/ 03 июня 2018

Нашел ответ.Это не заброшенный.Он просто все еще не сработал из-за слишком короткой жизни моей тестовой консоли.

Необработанное исключение будет выдано в GC.Collect ()

https://blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/

Во время GC он замечает, что никто никогда не проверял результат (и, следовательно, никогда не видел исключения), и поэтому рассматривает его как ненаблюдаемое исключение.

Таким образом, следующий код до конца основного метода решит проблему иЯ вижу исключение

GC.Collect();
Thread.Sleep(5000);
0 голосов
/ 02 июня 2018

Асинхронный код не обязательно является параллельным кодом, но вы все равно должны быть осторожны.

Это:

private async Task OnTestAsync()
{
    if (TestAsync != null)
        await TestAsync.Invoke(this);
}

может привести к неприятностям, потому что к тому времени, когда вызывается TestAsync.Invoke,TestAsync может быть нулевым.

Но проблема, которую вы пытаетесь решить, состоит не в том, что ожидается самый быстрый, а в том, что ожидается последний.

Вы должныпересмотреть свой API, но, если вы не можете, попробуйте это:

public class TestClass
{
    public delegate Task TestHandlerAsync(object sender);
    public event TestHandlerAsync TestAsync;

    private async Task OnTestAsync()
    {
        var testAsync = this.TestAsync;

        if (testAsync == null)
        {
            return;
        }

        await Task.WhenAll(
            from TestHandlerAsync d in testAsync.GetInvocationList()
            select d.Invoke(this));
    }

    public async Task TestTestAsync()
    {
        try
        {
            await OnTestAsync();
        }
        catch (Exception ex)
        {
            Program.ConsoleWriteLine($"{nameof(TestTestAsync)}: {ex.Message}", ConsoleColor.Green);
        }
    }
}

, если вы хотите показать только первое исключение.

Или:

public class TestClass
{
    public delegate Task TestHandlerAsync(object sender);
    public event TestHandlerAsync TestAsync;

    private async Task<Exception[]> OnTestAsync()
    {
        var testAsync = this.TestAsync;

        if (testAsync == null)
        {
            return new Exception[0];
        }

        return await Task.WhenAll(
            from TestHandlerAsync d in testAsync.GetInvocationList()
            select ExecuteAsync(d));

        async Task<Exception> ExecuteAsync(TestHandlerAsync d)
        {
            try
            {
                await d(this);
                return null;
            }
            catch (Exception ex)
            {
                return ex;
            }
        }
    }

    public async Task TestTestAsync()
    {
        try
        {
            var exceptions = await OnTestAsync();

            foreach (var exception in exceptions)
            {
                if (exception != null)
                {
                    Program.ConsoleWriteLine($"{nameof(TestTestAsync)}: {exception.Message}", ConsoleColor.Green);
                }
            }
        }
        catch (Exception ex)
        {
            Program.ConsoleWriteLine($"{nameof(TestTestAsync)}: {ex.Message}", ConsoleColor.Green);
        }
    }
}

если вы заботитесь обо всех.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...