C#多线程中的异常处理操作示例

作者:行走即歌 时间:2023-07-16 06:10:10 

本文实例讲述了C#多线程中的异常处理操作。分享给大家供大家参考,具体如下:

常规Thread中处理异常

使用Thread创建的子线程,需要在委托中捕捉,无法在上下文线程中捕捉


static void Main(string[] args)
{
 ThreadStart threadStart = DoWork;
 Thread thread = new Thread(threadStart);
 thread.Start();
 thread.Join();
}
static void DoWork()
{
 try
 {
   throw new Exception("子线程出现异常了");
 }
 catch (Exception ex)
 {
   Trace.Assert(false, "Catch In Delegate");
 }
}

Task中处理异常

1.仍然可以在委托中捕获异常

2.可以捕获Task.Wait() 或者 Task.Result 的 AggregateException 异常


try
{
 task.Wait();
}
catch (AggregateException ex)
{
 Console.WriteLine($"Error: {ex.GetType().Name}");
 foreach (Exception item in ex.InnerExceptions)
 {
   Console.WriteLine($"{item.GetType().Name}, {item.Message}");
 }
}

AggregateException 是并行任务中捕获的一组异常

通过延续任务捕获前驱任务中的异常


static void Main(string[] args)
{
 Task task = Task.Run(() => throw new Exception("前驱任务异常了"));
 Task faultedTask = task.ContinueWith(antecedentTask =>
 {
   antecedentTask.Exception.Handle(eachE =>
   {
     Console.WriteLine($"Error: {eachE.Message}");
     return true;
   });
 },TaskContinuationOptions.OnlyOnFaulted);
 faultedTask.Wait();
}

前驱任务:使用Run书写的第一个任务就是前驱任务

延续任务:在一个任务后使用ContinueWith添加的任务就是延续任务,延续一般是一个全新的工作线程

TaskContinuationOptions:指定延续任务时的可配置项,默认情况下前驱任务完成后,立即执行延续任务,OnlyOnFaulted表示只有前驱任务失败(出异常的时候)才会执行这一个延续任务

Task.Exception也是一个AggregateException 异常

注意:

1.当指定的TaskContinuationOptions与前驱任务运行结果不一致时,强制调用延续任务Wait()会引发TaskCanceledException异常


static void Main(string[] args)
{
 Task task = new Task(() =>
 {
   Console.WriteLine("前驱动任务执行中...");
 });
 Task faultedTask = task.ContinueWith(antecedentTask =>
 {
   Console.WriteLine("延续动任务执行中...");
 }, TaskContinuationOptions.OnlyOnFaulted);
 task.Start();
 try
 {
   faultedTask.Wait();
 }
 catch (AggregateException ex)
 {
   Console.WriteLine($"Error: {ex.GetType().Name}");
   foreach (Exception item in ex.InnerExceptions)
   {
     Console.WriteLine($"{item.GetType().Name}, {item.Message}");
   }
 }
 Console.WriteLine($"前驱任务状态{task.Status}");
 Console.WriteLine($"延续任务状态{faultedTask.Status}");
}

Ctrl+F5 输出

C#多线程中的异常处理操作示例

补充:

假如在前驱任务中出现了异常,如OnlyOnFaulted所愿,会执行faultedTask任务,并且在faultedTask.Wait()中不会捕捉到前驱任务的异常,具体看下面一点

2.延续任务虽然在异步任务中提供了类似if else 的ContinueWith但是在异常处理上还是有点局限,看一个例子


static void Main(string[] args)
{
 Task task = Task.Run(()
   =>
 throw new Exception("前驱任务异常了"));
 Task task1 = task.ContinueWith(antecedentTask =>
 {
   throw new Exception("延续任务1异常了");
 });
 Task task2 = task1.ContinueWith(antecedentTask =>
 {
   throw new Exception("延续任务2异常了");
 });
 Task task3 = task2.ContinueWith(antecedentTask =>
 {
   throw new Exception("延续任务3异常了");
 });
 try
 {
   task3.Wait();
 }
 catch (AggregateException ex)
 {
   Console.WriteLine($"Error: {ex.GetType().Name}");
   foreach (Exception item in ex.InnerExceptions)
   {
     Console.WriteLine($"{item.GetType().Name}, {item.Message}");
   }
 }
}

Ctrl+F5 输出

C#多线程中的异常处理操作示例

其实这样也可以理解,task3.Wait()只会收集task3所在工作线程上的异常,遗憾的是Task.Exception.InnerExceptions是一个只读集合,这样一来,每个任务的异常只能在各自委托中处理了,事实上也应该如此,可以使用TaskContinuationOptions进行灵活控制

使用CancellationTokenSource取消任务


static void Main(string[] args)
{
 CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
 cancellationTokenSource.Token.Register(() =>
 {
   Console.WriteLine("任务取消了");
 });
 cancellationTokenSource.CancelAfter(2000);
 Task task = Task.Run(() =>
 {
   while (true && !cancellationTokenSource.IsCancellationRequested)
   {
     Console.WriteLine("任务执行中...");
     Thread.Sleep(300);
   }
 },
 cancellationTokenSource.Token);
 task.Wait();
 Console.WriteLine($"任务的最终状态是:{task.Status}");
}

Ctrl+F5 输出

C#多线程中的异常处理操作示例

正常取消的任务最终状态是 RanToCompletion ,这里要注意的是,CancelAfter()是在这个方法调用的那一刻开始计时的(并非以Run开始计时,好吧,很好理解,我却疑惑了半天)

小结:

结合 TaskContinuationOptions 和 CancellationTokenSource 可以很好处理多任务中异常,但是编写在异步程序还是很繁琐的,具体的在下一个笔记中会结合C#5.0做一个比较

希望本文所述对大家C#程序设计有所帮助。

来源:https://www.cnblogs.com/cheesebar/p/6550887.html

标签:C#,多线程,异常处理
0
投稿

猜你喜欢

  • 详解SpringBoot通用配置文件(不定时更新)

    2022-12-01 09:07:04
  • Android硬件解码组件MediaCodec使用教程

    2023-03-14 01:35:36
  • 深入剖析Android系统中Service和IntentService的区别

    2023-06-29 17:55:16
  • JAVA 实现二叉树(链式存储结构)

    2022-02-18 11:35:21
  • 如何在C# 枚举中增加行为

    2022-10-28 06:11:34
  • Android中使用GridView实现仿微信图片上传功能(附源代码)

    2023-11-27 02:01:25
  • Java实现限定时间CountDownLatch并行场景

    2023-06-05 01:47:27
  • java反射机制给实体类相同字段自动赋值实例

    2023-11-25 19:52:29
  • 短网址的原理与生成方法(Java实现)

    2022-04-14 11:29:19
  • Spring Cloud详解实现声明式微服务调用OpenFeign方法

    2021-07-04 07:56:41
  • 详解Android中的ActivityThread和APP启动过程

    2021-08-20 22:51:25
  • Jackson中json格式的字符串与对象的互相转换方式

    2022-01-29 03:31:07
  • Android自定义View原理(实战)

    2021-07-25 02:46:47
  • 详解Java设计模式编程中的中介者模式

    2021-09-24 02:48:54
  • 浅谈Android硬件加速原理与实现简介

    2022-04-20 14:11:36
  • C#实现简单文本编辑器

    2022-04-28 06:42:30
  • SpringBoot整合Quartz实现定时任务详解

    2021-11-22 06:40:57
  • Android同时安装Release和Debug版本的方法

    2022-02-11 08:51:20
  • 以Java代码为例讲解设计模式中的简单工厂模式

    2023-02-09 15:14:17
  • c#典型工厂化实现实例

    2022-01-04 19:41:08
  • asp之家 软件编程 m.aspxhome.com