当前位置: 首页 > 编程笔记 >

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

郗丰
2023-03-14
本文向大家介绍C#多线程中的异常处理操作示例,包括了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#程序设计有所帮助。

 类似资料:
  • 本文向大家介绍Nodejs处理异常操作示例,包括了Nodejs处理异常操作示例的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了Nodejs处理异常操作。分享给大家供大家参考,具体如下: Exception.js optfile.js router.js 希望本文所述对大家nodejs程序设计有所帮助。

  • 本文向大家介绍Java基础之异常处理操作示例,包括了Java基础之异常处理操作示例的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了Java基础之异常处理操作。分享给大家供大家参考,具体如下: 示例代码: 抛出异常: 重新抛出异常: throws语句:一个方法产生自己不做处理的异常,用throws抛出到外层(谁调用,谁处理异常) 可以用一个catch()捕获多个异常: 自定义异常: 更多ja

  • 问题内容: 我更喜欢将异常处理逻辑放在main方法附近的调用堆栈中。我喜欢这种方法…但是,我创建了一个线程,该线程的run()内部的某些方法调用可能会引发异常。我真的很想看看是否有一种方法可以将这些异常返回到父线程?我能想到的最好的办法是在实现的对象内部设置一个变量。该变量是一个包含错误消息的字符串,该错误消息随后使用类加载器在父线程中正确地重新创建相同的异常。 我想知道的是,在这里得到想要的东西

  • 本文向大家介绍浅谈C#中简单的异常引发与处理操作,包括了浅谈C#中简单的异常引发与处理操作的使用技巧和注意事项,需要的朋友参考一下 异常和异常处理 C# 语言的异常处理功能可帮助您处理程序运行时出现的任何意外或异常情况。异常处理使用 try、catch 和 finally 关键字尝试某些操作,以处理失败情况,尽管这些操作有可能失败,但如果您确定需要这样做,且希望在事后清理资源,就可以尝试这样做。公

  • 主要内容:try/catch语句,C#中的异常类,自定义异常类,抛出异常在 C# 中,异常是在程序运行出错时引发的,例如以一个数字除以零,所有异常都派生自 System.Exception 类。异常处理则是处理运行时错误的过程,使用异常处理可以使程序在发生错误时保持正常运行。 C# 中的异常处理基于四个关键字构建,分别是 try、catch、finally 和 throw。 try:try 语句块中通常用来存放容易出现异常的代码,其后面紧跟一个或多个 catch 语句

  • 异常是程序在执行期间产生的问题。C++ 异常是指在程序运行时发生的特殊情况,比如尝试除以零的操作。 异常提供了一种转移程序控制权的方式。C++ 异常处理涉及到三个关键字:try、catch、throw。 throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。 catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。 try