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

本文实例讲述了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#程序设计有所帮助。

(0)

相关推荐

  • c#多线程网络聊天程序代码分享(服务器端和客户端)

    XuLIeHua类库 复制代码 代码如下: using System;using System.Collections;  using System.Collections.Generic;using System.Threading;  using System.Runtime.Serialization;using System.Runtime.Serialization.Formatters.Binary;using System.Text;using System.IO;using Sy

  • 浅谈C#多线程简单例子讲解

    .NET将关于多线程的功能定义在System.Threading名字空间中.因此,要使用多线程,必须先声明引用此名字空间(using System.Threading;). a.启动线程 顾名思义,"启动线程"就是新建并启动一个线程的意思,如下代码可实现: Thread thread1 = new Thread(new ThreadStart( Count)); 其中的 Count 是将要被新线程执行的函数. b.杀死线程 "杀死线程"就是将一线程斩草除根,为了不白

  • C#异常处理详解

    异常介绍 1.System.Exception类 Message属性:发生异常的原因和异常的内容 Souce属性:抛出异常程序集的名称 StackTrace属性:发生异常的方法调用情况 InnerException属性:次异常中包含的异常 2.try{}catch{}finally{} 处理异常 a.不带参数的catch和catch(Exception)是有区别的 catch(Exception)可以捕获所有以Exception类派生的异常,而不带参数的catch可以捕获所有异常,不管异常是不是

  • C#异常处理的一些经验和技巧

    1.什么时候该异常处理?1)代码最外层,如WinFrom,避免用户看到内部异常信息用户体验不好,或者造成程序崩溃.2)遇到异常需要恢复状态或者重试的地方.例如连接数据库偶然失败了,可以有个重连机制,在Catch块重新连接数据库.3)对于一系列有可能失败的任务,其中有一个任务失败,不想影响到其他任务.例如要上传100张图片,不想因为一张图片上传发生异常而失败,进而终止整个上传任务,仅需要记录下失败的图片,提醒用户重传即可.2.异常处理需要注意的地方1)Catch和Finally代码应该非常短,而且

  • C#异常处理中try和catch语句及finally语句的用法示例

    使用 try/catch 处理异常 try-catch 块的用途是捕捉和处理工作代码所生成的异常. 有些异常可以在 catch 块中处理,解决问题后不会再次引发异常:但更多情况下,您唯一能做的是确保引发适当的异常. 示例 在此示例中,IndexOutOfRangeException 不是最适当的异常:对本方法而言 ArgumentOutOfRangeException 更恰当些,因为错误是由调用方传入的 index 参数导致的. class TestTryCatch { static int G

  • c#使用多线程的几种方式示例详解

    (1)不需要传递参数,也不需要返回参数 ThreadStart是一个委托,这个委托的定义为void ThreadStart(),没有参数与返回值. 复制代码 代码如下: class Program { static void Main(string[] args) { for (int i = 0; i < 30; i++) { ThreadStart threadStart = new ThreadStart(Calculate); Thread thread = new Thread(thr

  • C#实现多线程下载文件的方法

    本文实例讲述了C#实现多线程下载文件的方法.分享给大家供大家参考.具体实现方法如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Threading; using System.Net; namespace WfpApp { public class MultiDownload { #region 变量 pri

  • C#异常处理总结及简单实例

    C#异常处理总结及简单实例 一.异常处理的理解? 异常处理是指程序在运行过程中,发生错误会导致程序退出,这种错误,就叫做异常. 因此处理这种错误,就称为异常处理. 二.异常处理如何操作? C# 异常处理时建立在四个关键词之上的:try.catch.finally 和 throw. 1.try:一个 try 块标识了一个将被激活的特定的异常的代码块.后跟一个或多个 catch 块. 2.catch:程序通过异常处理程序捕获异常.catch 关键字表示异常的捕获. 3.finally:finally

  • C#队列Queue多线程用法实例

    本文实例讲述了C#队列Queue多线程用法.分享给大家供大家参考.具体分析如下: 这里展示一个例子,供学习使用: private void button_测试Queue结合多线程_Click(object sender, EventArgs e) { Console.WriteLine("初始化队列"); queue = new Queue<string>(); string[] cars = new string[]{"宝马","奔驰&quo

  • C#多线程处理多个队列数据的方法

    本文实例讲述了C#多线程处理多个队列数据的方法.分享给大家供大家参考.具体实现方法如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Collections; using System.Windows.Forms; namespace ThredProcessQueue { //用于顯示狀態的代理

  • C#中的多线程多参数传递详解

    之前做了一个小的应用程序,用的是c#语言,涉及到了多线程的多参数传递,经过查找资料总结了一下解决方案! 第一种解决方案的原理是:将线程执行的方法和参数都封装到一个类里面.通过实例化该类,方法就可以调用属性来实现间接的类型安全地传递多个参数.看如下代码: 复制代码 代码如下: using System;using System.Threading; //ThreadWithState 类里包含了将要执行的任务以及执行任务的方法public class ThreadWithState {//要用到的

随机推荐