c# 基于任务的异步编程模式(TAP)的异常处理

作者:一只独行的猿 时间:2023-08-03 15:44:06 

在前面讲到了《基于任务的异步编程模式(TAP)》,但是如果调用异步方法,没有等待,那么调用异步方法的线程中使用传统的try/catch块是不能捕获到异步方法中的异常。因为在异步方法执行出现异常之前,已经执行完毕。

1、没有等待的调用异步方法

ThrowAfter方法是在一定延迟后抛出一个异常:


private async Task ThrowAfter(int ms,string message)
{
 await Task.Delay(ms);
 Console.WriteLine("异步任务随后将抛出异常。");
 throw new Exception(message);
}

DontHandle方法在调用异步方法时,由于有滞后性,所以使用try...catch...不能捕获到异步方法中的异常。


public void DontHandle()
{
 try
 {
   ThrowAfter(200, "异步方法抛出的异常");
 }
 catch(Exception ex)
 {
   Console.WriteLine(ex.Message);
 }
 Console.WriteLine("完成方法:DontHandle");
}

注意:返回void的异步方法不会等待。因为从async void方法抛出的异常无法捕获。因此,异步方法最好返回一个Task类型。

2、异步方法的异常处理

异步方法异常的比较好的处理方式使使用await关键字,将其放在try/catch语句中。


public async void HandleOneError()
{
 Console.WriteLine("HandleOneError方法开始执行。。。");
 try
 {
   await ThrowAfter(2000, "异步方法抛出的异常");
 }
 catch (Exception ex)
 {
   Console.WriteLine(ex.Message);
 }
 Console.WriteLine("完成方法:HandleOneError");
}

调用ThrowAfter方法后,HandleOneError会释放线程,但它会在任务完成时保持对任务的引用。当异步方法抛出异常,会调用匹配的catch块内的代码。

3、多个异步方法的异常处理

如果调用多个异步方法,会有多个抛出异常,在捕获异常时就会有问题。


public async void StartTwoTasks()
{
 Console.WriteLine("StartTwoTasks方法开始执行。。。");
 try
 {
   await ThrowAfter(2000, "first");//先执行该方法
   await ThrowAfter(1000, "Second");//第一个异步方法正常执行完后才会执行该方法
 }
 catch(Exception ex)
 {
   Console.WriteLine(ex.Message);
 }
 Console.WriteLine("完成方法:StartTwoTasks");
}

StartTwoTasks方法中,调用了两个异步方法。理论上认为,当第一个异步方法执行完,抛出异常后,紧接着就会调用第二个异步方法,并抛出异常。但实际上是第一个异步方法抛出异常之后,就会被catch捕获,并不会执行第二个异步方法。因为这种类型中,在“基于任务的异步编程模式(TAP)”一文中解释过,这种调用方法是等待第一个异步方法执行结束后,调用函数的线程控制权才会调用第二个异步方法,多个异步方法以此类推。但是当时我们使用了Task类中的WhenAll方法同时等待多个任务全部执行完,才执行后面的代码。


public async void StartTwoTasksParallel()
{
 Console.WriteLine("StartTwoTasksParallel方法开始执行。。。");
 try
 {
   Task t1 = ThrowAfter(2000, "first");//先执行该方法
   Task t2 = ThrowAfter(1000, "Second");//第一个异步方法执行完后才会执行该方法
   await Task.WhenAll(t1, t2);
 }
 catch (Exception ex)
 {
   Console.WriteLine(ex.Message);
 }
 Console.WriteLine("完成方法:StartTwoTasksParallel");
}

StartTwoTasksParallel方法使用Task类的WhenAll方法,并行调用两个不关联的异步方法。该方法将等待所有任务结束后才结束调用,不论任何一个抛出异常都不会影响其他任务。但是,该方法只会捕获第一个异常(先抛出异常的任务),其他异常将不会被显示。

有一种方法可以获取所有任务的异常信息,就是在try块外声明任务变量t1和t2,让这两个变量在catch块内访问。在catch块中检测任务的IsFaulted属性确认任务的状态,以判定是否出现异常,然后通过Task类的Exception.InnerException访问异常信息本身。

4、使用AggregateException信息

Task.WhenAll方法返回一个Task的结果变量。catch语句只会捕捉到所有异步任务中的第一个异常,但是Task.WhenAll方法返回的Task类型结果变量中会包含所有任务都出现的异常。外部结果任务的Exception属性是一个AggregateException类型,显示所有异常只需要遍历结果任务中的Exception的InnerExceptions属性即可。


public async void ShowAggregatedException()
{
 Console.WriteLine("ShowAggregatedException方法开始执行。。。");
 Task taskResult = null;
 try
 {
   Task t1 = ThrowAfter(2000, "first");//先执行该方法
   Task t2 = ThrowAfter(1000, "second");//第一个异步方法执行完后才会执行该方法
   Task t3 = ThrowAfter(1500, "third");//第一个异步方法执行完后才会执行该方法
   await (taskResult = Task.WhenAll(t1, t2, t3));
 }
 catch (Exception ex)
 {
   Console.WriteLine("handle {0}",ex.Message);
   foreach (Exception ex1 in taskResult.Exception.InnerExceptions)
   {
     Console.WriteLine("Inner exception {0}", ex1.Message);
   }
 }
 Console.WriteLine("完成方法:ShowAggregatedException");
}

来源:https://www.cnblogs.com/pilgrim/p/9147840.html

标签:c#,异步编程,异常
0
投稿

猜你喜欢

  • Android自定义文件路径选择器

    2022-11-09 03:04:07
  • Kotlin利用Regex如何构建正则表达式详解

    2022-12-25 18:31:10
  • C#图像亮度调式与伪彩色图的处理教程(推荐)

    2022-03-26 19:43:54
  • Spring Security OAuth2认证授权示例详解

    2022-09-11 19:45:47
  • Java 网络编程总结

    2023-11-10 22:19:29
  • Android中Volley框架下保持会话方法

    2022-09-11 00:44:12
  • 常用的Java数据结构知识点汇总

    2022-09-09 02:26:02
  • C++ 先对数组排序,在进行折半查找

    2021-07-07 09:03:18
  • 解决问题:Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources

    2023-11-23 23:40:39
  • C# 生成随机数的代码

    2021-06-16 06:05:43
  • Unity登录注册时限制发送验证码次数功能的解决方法

    2021-12-28 00:27:12
  • Java类中this关键字与static关键字的用法解析

    2023-11-09 22:45:19
  • Ajax 验证用户输入的验证码是否与随机生成的一致

    2022-06-29 00:43:32
  • Java实现将彩色PDF转为灰度PDF的示例代码

    2022-11-26 19:47:41
  • C# 下载文件 删除文件 写入文本的实例

    2022-11-27 18:34:25
  • java数字转汉字工具类详解

    2023-04-28 02:00:26
  • 1小时快速上手RabbitMQ(简介及安装过程)

    2022-04-26 15:49:40
  • 深入理解Java并发编程之LinkedBlockingQueue队列

    2022-10-02 06:35:22
  • Java基本类型与byte数组之间相互转换方法

    2023-11-16 22:49:23
  • C#调用JS的几种方法

    2022-09-29 23:24:18
  • asp之家 软件编程 m.aspxhome.com