未捕获或处理异步处理程序中抛出的异常

时间:2018-06-02 16:20:40

标签: c# asynchronous exception async-await unhandled-exception

出于某种原因,我无法捕获订阅事件的匿名异步委托中抛出的异常。

它不会被TestTestAsync内部捕获(我想因为调用仅等待最快的一次)但是为什么它没有被捕获到未处理或未观察到或崩溃的应用程序中?

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 :(得分:0)

异步代码不一定是并发代码,但无论如何你应该小心。

此:

app.post('/results', async (req, res) => {
  // ...

  Promise.all(
    lookup('prices', products, countries),
    lookup('descriptions', products, countries),
    lookup('other', products, countries)
  )
    .then(values => get_table_data(...values)) // [price_data, description_data, other_data]
    .then(table_data => res.render('results.ejs', { table_data }));
});

可能会让您遇到麻烦,因为在调用private async Task OnTestAsync() { if (TestAsync != null) await TestAsync.Invoke(this); } 时,TestAsync.Invoke可以为空。

但是你要解决的问题是,不是等待最快的问题,而是等待最后一个问题。

您应修改您的API,但如果您不能,请尝试以下操作:

TestAsync

如果您只关心显示第一个例外。

或者:

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);
        }
    }
}

如果你关心所有人。

答案 1 :(得分:0)

找到答案。它没有放弃。由于我的测试控制台的寿命太短,它仍然没有被解雇。

GC.Collect()

将抛出未处理的异常

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

  

在GC期间,它注意到没有人检查过结果(和   因此从未见过例外情况)因此将其作为一个例子   未被观察到的例外。

因此,在main方法结束之前的下一个代码将解决问题并且我看到异常

{{1}}