首页 > 编程 > C# > 正文

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

2020-01-24 00:05:23
字体:
来源:转载
供稿:网友

本文实例讲述了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 输出

补充:

假如在前驱任务中出现了异常,如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 输出

其实这样也可以理解,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 输出

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

小结:

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

更多关于C#相关内容感兴趣的读者可查看本站专题:《C#常见控件用法教程》、《WinForm控件用法总结》、《C#数据结构与算法教程》、《C#面向对象程序设计入门教程》及《C#程序设计之线程使用技巧总结

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

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表