常规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();
Task task = Task.Run(() =>
{
while (true && !cancellationTokenSource.IsCancellationRequested)
{
Console.WriteLine("任务执行中...");
Thread.Sleep();
}
},
cancellationTokenSource.Token);
task.Wait();
Console.WriteLine($"任务的最终状态是:{task.Status}");
}

Ctrl+F5 输出

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

小结:


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

原文链接

C#多线程中的异常处理(转载)的更多相关文章

  1. C#多线程中的异常处理

    常规Thread中处理异常 使用Thread创建的子线程,需要在委托中捕捉,无法在上下文线程中捕捉 static void Main(string[] args) { ThreadStart thre ...

  2. Java多线程中的异常处理

    在java多线程程序中,所有线程都不允许抛出未捕获的checked exception,也就是说各个线程需要自己把自己的checked exception处理掉.这一点是通过java.lang.Run ...

  3. C++ std::thread 多线程中的异常处理

    环境: VS2019 包含头文件: #include <iostream>#include<thread>#include<exception> 线程函数采用try ...

  4. ASP.NET Web API 中的异常处理(转载)

    转载地址:ASP.NET Web API 中的异常处理

  5. 关于C#中async/await中的异常处理(上)-(转载)

    在同步编程中,一旦出现错误就会抛出异常,我们可以使用try…catch来捕捉异常,而未被捕获的异常则会不断向上传递,形成一个简单而统一的错误处理机制.不过对于异步编程来说,异常处理一直是件麻烦的事情, ...

  6. c#初学-多线程中lock用法的经典实例

    本文转载自:http://www.cnblogs.com/promise-7/articles/2354077.html 一.Lock定义     lock 关键字可以用来确保代码块完成运行,而不会被 ...

  7. C++中的异常处理(三)

    C++中的异常处理(三) 标签: c++C++异常处理 2012-11-24 23:00 1520人阅读 评论(0) 收藏 举报  分类: 编程常识(2)  版权声明:本文为博主原创文章,未经博主允许 ...

  8. C++中的异常处理(二)

    C++中的异常处理(二) 标签: c++C++异常处理 2012-11-24 20:56 1713人阅读 评论(2) 收藏 举报  分类: C++编程语言(24)  版权声明:本文为博主原创文章,未经 ...

  9. C++中的异常处理(一)

     来自:CSDN 卡尔  后续有C++中的异常处理(二)和C++中的异常处理(三),C++中的异常处理(二)是对动态分配内存后内部发生错误情况的处理方法,C++中的异常处理(三)中是使用时的异常说明. ...

随机推荐

  1. React中使用百度地图API

    今天我们来搞一搞如何在React中使用百度地图API好吧,最近忙的头皮发麻,感觉身体被掏空,所以很久都没来写博客了,但今天我一定要来一篇好吧 话不多说,我们直接开始好吧 特别注意:该React项目是用 ...

  2. “一切都是消息”--iMSF(即时消息服务框架)入门简介

    “一切都是消息”--这是iMSF(即时消息服务框架)的设计哲学. MSF的名字是 Message Service Framework 的简称,由于目前框架主要功能在于处理即时(immediately) ...

  3. MySQL 性能优化--优化数据库结构之优化数据大小

    MySQL性能优化--优化数据库结构之优化数据大小   By:授客  QQ:1033553122 尽量减少表占用的磁盘空间.通常,执行查询期间处理表数据时,小表占用更少的内存. 表列 l   尽可能使 ...

  4. Android basics

    只要是Android中的控件,最终都继承自View.

  5. maven(四):一个基本maven项目的pom.xml配置

    继续之前创建的test项目,一个基本项目的pom.xml文件,通常至少有三个部分 第一部分,项目坐标,信息描述等 <modelVersion>4.0.0</modelVersion& ...

  6. Django2.0.1开发框架搭建

    1.使用vs2017创建空白django项目 2.右键python环境的env---安装python包  升级django到2.0.1和setuptools到38.4.0版本,具体环境如下: 3.配置 ...

  7. flask框架的教程--虚拟环境的安装[一]

    为什么需要安装虚拟环境呢? 因为虚拟环境中扩展包对本机软件中不影响 安装 查看是否安装了虚拟环境 安装虚拟环境的第三方工具 sudo pip install virtualenv sudo pip i ...

  8. Linux 小知识翻译 - 「别名」

    实际上,「别名」被用在多种场合下.比如「命令的别名」,「邮件地址的别名」等等. 所以,单独说「别名」的时候,根据不用的场合,代表的意思也不一样. 一般来说,「别名」是指意思差不多的东西. 「别名」的英 ...

  9. vue学习笔记1-基本知识

    1.npm 安装node.js的时候会一起安装npm包管理器,能够解决nodejs代码部署问题,常见使用如下: 允许用户从npm服务器下载别人编写的第三方包到本地应用允许用户从npm服务器下载并安装别 ...

  10. 【17】有关python面向对象编程的提高【多继承、多态、类属性、动态添加与限制添加属性与方法、@property】

    一.多继承 案例1:小孩继承自爸爸,妈妈.在程序入口模块再创建实例调用执行 #father模块 class Father(object): def __init__(self,money): self ...