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();
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#多线程中的异常处理的更多相关文章
- C#多线程中的异常处理(转载)
常规Thread中处理异常 使用Thread创建的子线程,需要在委托中捕捉,无法在上下文线程中捕捉 static void Main(string[] args) { ThreadStart thre ...
- Java多线程中的异常处理
在java多线程程序中,所有线程都不允许抛出未捕获的checked exception,也就是说各个线程需要自己把自己的checked exception处理掉.这一点是通过java.lang.Run ...
- C++ std::thread 多线程中的异常处理
环境: VS2019 包含头文件: #include <iostream>#include<thread>#include<exception> 线程函数采用try ...
- web应用中的异常处理
楼主前几天写了一篇“Java子线程中的异常处理(通用)”文章,介绍了在多线程环境下3种通用的异常处理方法. 但是平时大家的工作一般是基于开发框架进行的(比如Spring MVC,或Spring Boo ...
- 编写高质量代码改善C#程序的157个建议——建议85:Task中的异常处理
建议85:Task中的异常处理 在任何时候,异常处理都是非常重要的一个环节.多线程与并行编程中尤其是这样.如果不处理这些后台任务中的异常,应用程序将会莫名其妙的退出.处理那些不是主线程(如果是窗体程序 ...
- 编写高质量代码改善C#程序的157个建议——建议66:正确捕获多线程中的异常
建议66:正确捕获多线程中的异常 多线程的异常处理需要采用特殊的方式.一下这种方式会存在问题: try { Thread t = new Thread((ThreadStart)delegate { ...
- 转:Spring Boot应用中的异常处理
引自:https://www.cnblogs.com/yangfanexp/p/7616570.html 楼主前几天写了一篇“Java子线程中的异常处理(通用)”文章,介绍了在多线程环境下3种通用的异 ...
- 【repost】JS中的异常处理方法分享
我们在编写js过程中,难免会遇到一些代码错误问题,需要找出来,有些时候怕因为js问题导致用户体验差,这里给出一些解决方法 js容错语句,就是js出错也不提示错误(防止浏览器右下角有个黄色的三角符号,要 ...
- c#初学-多线程中lock用法的经典实例
本文转载自:http://www.cnblogs.com/promise-7/articles/2354077.html 一.Lock定义 lock 关键字可以用来确保代码块完成运行,而不会被 ...
随机推荐
- Jquery测试题
一.Jquery测试题 下面哪种不是jquery的选择器?(单选) A.基本选择器 B.后代选择器 C.类选择器 D.进一步选择器 考点:jquery的选择器 (C) 当DOM加载完成后要执行的函数, ...
- SQL解决"双重职位的查询"
双重身份问题: create table role_tab ( person char(5) not null, role char(1) not null ) insert into role_t ...
- Manacher算法解析
Manacher算法 Manachar算法主要是处理字符串中关于回文串的问题的,它可以在 O(n) 的时间处理出以字符串中每一个字符为中心的回文串半径,由于将原字符串处理成两倍长度的新串,在每两个字符 ...
- Jmeter——HTTP协议的接口压力测试环境搭建
文章版权由作者小小小丝和博客园共有,若转载请于明显处标明出处:http://rpc.cnblogs.com/metaweblog/xxxs JDK 是整个Java的核心,包括了Java运行环境.Ja ...
- Scala编程入门---数组操作之Array.ArrayBuffer以及遍历数组
在Scala中,Array代表的含义与Java类似,也是长度不可改变的数组.此外,由于Scala与java都是运行在JVM中,双方可以互相调用,因此Scala数组底层实际上是java数组.列如字符串数 ...
- swagger在nginx下出现无法请求接口的问题
在Nginx配置绑定域名的时候,增加proxy_set_header即可 示例如下: server { listen ; server_name xxx.dev.internal.XXX.com; l ...
- 如何找某个样式属于哪个Element
如果找不到样式所在的Element,那么可以参考排除法,逐个删除覆盖在同一位置的元素,如果该样式消失,那么可以判断为这个样式.
- cocos2d-x高级学习
弱联网开发技术: libcurl 添加lib文件:libcurl_imp.lib pthreadVCE2.lib 添加头文件:#include"curl/curl.h" curl ...
- cnblog 模板 SimpleMemory 个性化设置代码备份
/页面顶部作者名/ blogTitle h1 { font-size: 50px; margin-top: 0px; } /页面简介/ blogTitle h2 { letter-spacing: 1 ...
- 【Service Fabric】小白入门记录 本地Service Fabric集群安装及设置
本篇内容是自学自记,现在我还不知道Service Fabric究竟是怎么个入门法,反正按照入门教程先进行本地Service Fabric集群的安装,万里路始于足下,要学习总得先把环境装好了才能开始学习 ...