C#无await的async方法异常未捕获及UnobservedTaskException不触发问题
问题1:未加await的async Task方法异常无法被外层catch捕获的原因
只要方法被async Task修饰,无论内部是否包含await关键字,编译器都会对方法做状态机改写:方法执行过程中抛出的异常不会直接向上抛给调用方,而是会被封装到方法返回的Task对象中,同时将该Task标记为Faulted状态。
你调用ThrowsAsync()时没有加await,相当于直接丢弃了返回的Task对象,外层try/catch只能捕获方法调用过程中同步抛出的异常,被打包到Task中的异常不会同步触发,因此不会被catch块捕获。
如果你需要该方法的异常和普通同步方法一样直接向上抛出,只需要去掉方法的async关键字即可,无返回值的场景下直接返回Task.CompletedTask,此时抛出的异常就属于同步异常,可以被外层catch捕获。
问题2:UnobservedTaskException事件未触发的原因
TaskScheduler.UnobservedTaskException的触发有两个必要前提:
- 封装了异常的故障Task对象被GC回收
- 回收之前从未对该Task的异常做过观察操作(观察操作包括await、访问Task的
Exception属性、调用Wait()方法等)
你的代码中Console.ReadLine()会一直阻塞主线程,程序既没有退出也没有触发GC回收动作,封装了异常的Task对象仍存活在内存中,自然不会触发未观察异常事件。你可以在原有调用逻辑后主动触发GC验证:
try { ThrowsAsync(); } catch (Exception ex) { Console.WriteLine($"Caught in try/catch : {ex.Message}"); } // 新增GC触发代码 GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
运行后即可看到UnobservedTaskException事件的输出内容。
另外注意:.NET Core 2.0及以上版本(包括你使用的.NET 5)中,未观察的Task异常默认不会导致进程崩溃,仅会触发该事件,和.NET Framework的旧行为存在差异。
内容的提问来源于stack exchange,提问作者mnj
相关产品推荐
相关产品推荐

