C#异步Task异常未传至Main方法、任务提前执行问题咨询
代码参考
以下为Main方法实现代码:
static void Main(string[] args) { var taskList = new List<Task<bool>>(); try { for (var i = 0; i < 10; i++) { taskList.Add(Processor.Process(i)); } } catch (Exception e) { Console.WriteLine(e); throw; } }
对应的Processor类及Process方法实现代码如下:
public static class Processor { public static async Task<bool> Process(int num) { if (num % 2 == 0) { Console.Write(num); } else { throw new Exception("Hello Exception"); } await Task.Delay(1); return true; } }
上述程序运行后的输出结果为02468,针对两个问题的解答如下:
问题1:传入奇数参数时Process方法抛出的异常为何没有传递到Main方法的try-catch块中被捕获
核心原因有两点:
- 返回
Task/Task<T>的async方法,无论异常抛出位置是在第一个await之前还是之后,都会被async状态机自动捕获,封装到返回的Task对象中,不会直接以同步异常的形式逃逸到调用方的调用栈上。现有代码中的try-catch只能捕获调用Processor.Process(i)时同步抛出的、未被封装的异常,这类异常在返回Task的async方法中不会存在。 - 被封装到Task中的异常,只有在主动
await该Task、或者调用Task.Wait()/访问Task.Result这类会阻塞等待任务完成的成员时,才会被重新抛出。现有代码只是把Task存入列表,既没有await任何任务,Main方法执行完for循环后就直接运行结束、进程退出,这些存放在Task中的异常完全没有触发重抛的时机,自然不会进入catch块。.NET Core 2.0及以上版本默认不会因为未被观察的Task异常导致进程崩溃,没有注册TaskScheduler.UnobservedTaskException事件的情况下,这类异常会被静默忽略。
问题2:Task加入列表即开始执行的底层原因,以及暂缓任务批量启动的实现方式
首先明确:C#中默认创建的Task都是热任务(Hot Task),创建即启动,await的作用是等待已运行的任务完成、解包返回结果/异常,从来不是启动任务的开关。
底层逻辑是:.NET的Task默认遵循承诺模型,返回Task的方法代表「一个已经启动的正在进行的操作」,调用这类方法时,方法内部的代码会同步执行到第一个await的位置,完成后续调度后立刻返回代表该运行中操作的Task对象,和后续是否await没有关系。代码中能直接输出02468,就是因为Processor.Process(i)调用时,判断奇偶、Console.Write、奇数分支抛异常的逻辑都是在调用线程上同步执行的,根本不需要等await触发。
如果要实现「暂存任务逻辑,收到统一指令后再批量启动」,不要直接存储Task对象,改为存储Func<Task>/Func<Task<T>>类型的委托即可——委托只是对执行逻辑的封装,调用委托前不会执行任何内部代码。参考实现如下:
// C# 7.1及以上版本支持async Task作为程序入口 static async Task Main(string[] args) { // 存储任务工厂委托,此时不会执行Process方法的任何逻辑 var taskFactories = new List<Func<Task<bool>>>(); for (var i = 0; i < 10; i++) { // 缓存迭代变量,避免闭包捕获迭代变量导致的参数错误 int num = i; taskFactories.Add(() => Processor.Process(num)); } try { // 收到启动指令后,批量调用委托生成Task(此时任务才真正启动),再等待所有任务完成 var runningTasks = taskFactories.Select(factory => factory()).ToList(); var results = await Task.WhenAll(runningTasks); } catch (Exception e) { // 此处可以正常捕获到Process方法抛出的异常 Console.WriteLine(e); } }
内容的提问来源于stack exchange,提问作者user584018
相关产品推荐
相关产品推荐

