在一些并行操作或者任务列表执行的过程中,会需要用到AggregateException进行聚合异常的处理 ---- 对于不同类型的异常我们可能期望不同的处理方式,或者简单的对每个内部异常进行日志输出 但是如果AggregateException的内部也嵌套了AggregateException那么就很尴尬了 我们必须使用while循环进行处理 不过AggregateException提供了一个简单的解决方案 ,就是Flatten方法 Flatten方法可以将AggregateException以迭代的方式展开,所有的InnerException,以列表的形式进行单独处理哦(如微软的例子所示) public TaskCreationOptions.AttachedToParent); }); try { task1.Wait(); } catch (AggregateException Docs ---- 本文会经常更新,请阅读原文: https://xinyuehtx.github.io/post/%E4%BD%BF%E7%94%A8flatten%E5%B1%95%E5%BC%80AggregateException.html
在一些并行操作或者任务列表执行的过程中,会需要用到AggregateException进行聚合异常的处理 ---- 由于是聚合异常,他可能包含许多不同类型的内部异常。 但是另一部分我们却期望将其重新抛出 对于这种需求有一种简单的解决方案AggregateException的Handle方法 public async void Foo() { try ; } ); } catch (AggregateException ae) { ae.Handle(ex => { if (ex is CustomException 这样就能优雅的处理不同的内部异常 参考链接: AggregateException.Handle(Func) Method (System) - Microsoft Docs ---- 本文会经常更新, 请阅读原文: https://xinyuehtx.github.io/post/%E4%BD%BF%E7%94%A8AggregateException%E7%9A%84Handle%E8%BF%87%
阻塞线程式
我们可以使用Wait(),WaitAny(),WaitAll()来捕获Task的异常,详见下图:
捕获Task异常,准确来说要用AggregateException类,右边是运行结果 TaskExceptionEventArgs:EventArgs
{
///
,而在 AggregateException 的内部又有一个 InnerExceptions 属性用来包装所有异常的集合。 对于使用 await 修饰符和调用 Wait() 方法、访问 Result 属性对于异常的捕获是有区别的: Wait() 、Result 当使用Wait 或 Result 的时候,异步方法是将自身的 AggregateException async Task Main(string[] args) { try { TestExceptionAsync().Wait(); } catch (AggregateException aggregateException) { foreach (var ex in aggregateException.InnerExceptions) { (Test TestExceptionAsync) await 使用 await 修饰符,发生异常的时候,抛出的不是 AggregateException 对象,而是 AggregateException
不过,还是有一点小小的区别的:如果任务失败,Task.GetAwaiter().GetResult()会直接抛出异常,而Task.Result则会把异常包装在AggregateException中。 毕竟它少了异常的包装操作,即直接抛出异常,而不是把异常包装在AggregateException中。 但是,您可能会发现自己处在某些高级情况下,这些情况下您想要的行为类似于所采用的同步阻塞Task.Wait,但是您希望将原始异常展开而不是传播,而不是将其封装在AggregateException中。 在那些罕见的情况下,我的首选方法是GetAwaiter().GetResult()因为它保留任务异常,而不是将它们包装在中AggregateException。
使用AggregateException信息 除了上述方式外,还有一种更好的获取所有任务的异常信息的方式,Task.WhenAll() 方法返回的结果其实也是一个 Task 对象,而 Task 有一个 Exception 属性,它的类型是 AggregateException,是 Exception的一个派生类,AggregateException 类有一个 InnerExceptions 属性(异常集合 如果任何一个异步操作失败,WhenAll 方法将返回一个 AggregateException 对象,其中包含所有失败的异常。 如果第一个操作失败,WhenAny 方法将返回一个 AggregateException 对象,其中包含第一个失败的异常。
如果任何任务失败,Task.WhenAll本身将抛出AggregateException。以下是如何以健壮的方式处理异常。 Task.WhenAll的异常处理 关键点: AggregateException: 当Task.WhenAll完成时,如果任何任务失败,它会抛出一个AggregateException。 处理单个任务异常: 你可以遍历AggregateException的InnerExceptions属性来处理每个单独的异常。 )) }; try { await Task.WhenAll(tasks); } catch(AggregateException TaskContinuationOptions.OnlyOnFaulted) }; try { await Task.WhenAll(tasks); } catch(AggregateException
来看下这段代码: static void Main(string[] args) { //1000000000这个数字会抛出System.AggregateException 你的代码就永远注意不到这个异常的发生,如果不能捕捉到这个异常,垃圾回收时,抛出AggregateException,进程就会立即终止,这就是“牵一发动全身”,莫名其妙程序就自己关掉了,谁也不知道这是什么情况 一旦引发,就会向你的时间处理器方法传递一个UnobservedTaskExceptionEvenArgs对象,其中包含了你没有注意的AggregateException。 Console.WriteLine("This sum is:" + t.Result); } catch (AggregateException //其他任何异常都造成抛出一个AggregateException,其中 //只包含未处理的异常 x.Handle(e => e is
System.AggregateException:当 actions 数组中的任何操作引发异常时引发的异常。 System.AggregateException:包含在所有线程上引发的全部单个异常的异常。 System.AggregateException:包含了所有线程上引发的全部单个异常。 并行循环运行的过程中,可能有多个迭代抛出异常,所以一般使用AggregateException来捕获异常。AggregateException继承自Exception。 为了防止仅使用AggregateException未能捕获某些异常,使用AggregateException的同时还要使用Exception。
Exception 获取导致 AggregateException 提前结束的 Task。 如果任务已被取消,System.AggregateException将包含其 System.AggregateException.InnerExceptions 集合中的 System.OperationCanceledException AggregateException:在至少一个 Task 实例已取消。 如果任务已被取消, AggregateException 异常包含 OperationCanceledException 中的异常其 AggregateException.InnerExceptions System.AggregateException:聚合异常包含由相关联的 System.Threading.CancellationToken 上已注册的回调引发的所有异常。
Thread.Sleep(retryInterval); } } throw new AggregateException Thread.Sleep(retryInterval); } } throw new AggregateException Thread.Sleep(retryInterval); } } throw new AggregateException Thread.Sleep(retryInterval); } } throw new AggregateException Thread.Sleep(retryInterval); } } throw new AggregateException
Console.WriteLine("Error when DeleteFolder"); Console.WriteLine(new AggregateException (exceptionList)); } } 注意我这里没有输出错误,也许你需要修改Console.WriteLine(new AggregateException
throw new AggregateException(exceptions); 于是两边的调用栈就被分别保留在了多个不同的 Exception 实例中。然而看异常总要一层层点开查看,始终不便。 ExceptionDispatchInfo.Capture(exceptions.First()).Throw(); } else if(exceptions.Count > 1) { throw new AggregateException 而这些,正是 Task 管理异步线程异常时采用的策略——单个异常直接在调用线程直接抛出,多个异常抛出 AggregateException。
localDownloadPath, overWrite, response); 34: } 35: catch (AggregateException aggregateException) 36: { 37: Console.ForegroundColor = ConsoleColor.Red; 38: Console.WriteLine(string.Format("Exception : ", aggregateException aggregateException) 52: { 53: ConsoleColor.Red; 54: Console.WriteLine(string.Format("Exception : ", aggregateException
this.source).ConfigureAwait(false).GetAwaiter().GetResult(); 31: } 32: catch (AggregateException aggregateException) 33: { 34: throw aggregateException.InnerException; 35
CancellationToken,然后在需要取消的时候调用ThrowIfCancellationRequested方法,这样会抛出一个OperationCanceledException,并被封装到Task的AggregateException 这样会抛出一个AggregateException异常,真正引发的异常可以由GetBaseException方法获得。 ."); cts.CancelAfter(3000); try { Console.WriteLine($"计算结果是:{sumResult.Result}"); } catch (AggregateException
Wait 针对无返回值,可以帮助捕获到;ExceptionResult 针对有返回值,可以帮助捕获到Exception; 问题2:为什么得到的是AggregateException异常? 而AggregateException相当于做了一个聚合,将所有Exception的Message组合在一起。 问题3:延续任务中的异常又该如何捕获?
代码如下: Task mytask = Task.Run(()=> { throw null; }); try { mytask.Wait(); } catch (AggregateException NullReferenceException) { Console.WriteLine("null"); } else { throw; } } CLR将异常包裹在AggregateException 但调用GetResult的好处是,如果task发生故障,那么异常会被直接的抛出,而不是包裹在AggregateException里面,这样的话catch快就简洁了很多。 但是,必须直接处理AggregateException: 如果task发生故障,需要额外的代码来吧Continuation封装(marshal)到UI应用上。
task.Result)); somethingElse.Wait(); Console.WriteLine(); } 异步方法的异常处理 一般情况下使用Task的时候如果抛出异常,Task会抛出一个AggregateException 而使用异步方法的时候,为了提供与同步方法相似的编程体验,当抛出异常的时候会直接抛出原始异常而不是AggregateException异常。 首先先来定义一个返回Task的会抛出异常的方法。
Exception:获取导致任务失败的AggregateException类型的异常。如果任务成功完成,则返回null。 int result =await task; Console.WriteLine($"任务成功完成,结果:{result}"); } catch(AggregateException calculationTask; 任务中的异常可以通过等待任务时捕获,或者在任务完成后检查其 Exception 属性来处理: try { await task; } catch (AggregateException 使用 try-catch 块捕获并检查 AggregateException 的内部异常。 使用取消令牌 在长时间任务中实现取消,允许用户取消可能耗时的操作。