本文记录 dotnet 的一个令人迷惑的设计,在 Task 里,有一个叫 ContinueWith 的方法,此方法可以在 Task 完成时执行传入的委托。在 ContinueWith 方法里面,还有一个可选的 TaskContinuationOptions 参数,在此参数里面传入 OnlyOnFaulted 即可在 Task 出错时才执行传入的委托,然而此行为迷惑的是在 Task 正在执行完成却抛出取消异常

在等待任务执行完成之后,干某个活的事情上,有多个可选方法。一个就是老实使用 await 等待 Task 执行完成,然后再继续编写后续逻辑,如以下代码

await task;
干自己的活();

另一个方法就是通过 ContinueWith 方法,比如在使用 Task.Run 执行某个 Foo 方法之后,再 干自己的活 的代码

        var task = Task.Run(Foo).ContinueWith(t =>
{ });

以上的 ContinueWith 方法里面传入的委托是不管 Task 的执行状态,无论是成功还是失败都能进入。如果只期望只有在失败时才进入,可以传入 OnlyOnFaulted 参数,代码如下

        var task = Task.Run(Foo).ContinueWith(t =>
{ }, TaskContinuationOptions.OnlyOnFaulted);

然而这里存在一个令人迷惑的行为,大家猜猜,当 Foo 正常执行时,等待上面代码的 task 时,是否会抛出异常

答案是抛出 TaskCanceledException 异常

        var task = Task.Run(Foo).ContinueWith(t =>
{ }, TaskContinuationOptions.OnlyOnFaulted); try
{
await task;
}
catch (TaskCanceledException e)
{ } static void Foo()
{ }

这是因为 dotnet 认为 ContinueWith 里面的委托被取消了

那如果 Task 执行过程中抛出异常呢?看看下面的代码

        var task1 = Task.Run(FooWithException).ContinueWith(t =>
{ }, TaskContinuationOptions.OnlyOnFaulted); await task1; static void FooWithException()
{
throw new Exception("lindexi is doubi");
}

可以看到 task1 正常被等待,啥事都没有发生

这么特别诡异起来了,很好就在代码里面挖坑。毕竟写了以上代码的开发者更多的是进行测试 Task 异常的情况。再加上如果偶尔的正常执行完成,抛出的是取消异常,很多开发者都会以为是正常被取消而已

也有伙伴说,那分开两个 Task 等待好了,如以下代码

        var task = Task.Run(Foo);

        var task1 = task.ContinueWith(t =>
{ }, TaskContinuationOptions.OnlyOnFaulted);

但是以上代码解决不了的问题是,如果期望等待整个大的 Task 执行完成,也就是 Task 和 ContinueWith 里面的内容全部执行完成,那这个逻辑就诡异了

也就是只有在无需等待 ContinueWith 执行结果的情况下,才可以推荐使用 OnlyOnFaulted 参数。没有等待 ContinueWith 执行结果,且刚好 Task 是正常执行的,这是不会将取消异常抛到 UnobservedTaskException 里的

        TaskScheduler.UnobservedTaskException += (sender, eventArgs) =>
{
};

在 dotnet 的设计里面,如果一个 Task 存在异常,且这个 Task 的异常没有被任何代码捕获到,将在此 Task 被 GC 时,抛到 UnobservedTaskException 里面。可以通过如上代码的事件,获取到是否存在有 Task 的异常没有被捕获。进入 UnobservedTaskException 事件的异常不会导致应用挂掉,只是用来记录日志或者埋点上报等,让开发者知道有某个 Task 的异常没有被捕获

本文的代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin f758059f3f9f9bbfe2e7205a137d2e5b3da31f7a

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin f758059f3f9f9bbfe2e7205a137d2e5b3da31f7a

获取代码之后,进入 HojohoyahobaWayfahurhalqeje 文件夹

更多博客,请参阅我的 博客导航

dotnet 警惕 Task 的 ContinueWith 带上 OnlyOnFaulted 参数抛出取消异常的更多相关文章

  1. js进阶解决浏览器缓存不能自动更新的问题(在ajax的url上带上一个参数,可以是日期,或者是随机数)(随机数Math.random)(取得日期的毫秒数:new Date().getTime();)

    js进阶解决浏览器缓存不能自动更新的问题(在ajax的url上带上一个参数,可以是日期,或者是随机数)(随机数Math.random)(取得日期的毫秒数:new Date().getTime();) ...

  2. 在XP系统中自带的 msvcrt.dll 和 Vista 中的 msvcrt.dll 版本不同,导致抛出的异常不同

    然而,在XP系统中,系统自带的 msvcrt.dll 和 Vista 中的 msvcrt.dll 版本不同, 并没有这个 _except_handler4_common ,结果就出现了启动程序时,遇到 ...

  3. 记一次asp.net core 在iis上运行抛出502.5错误

    asp.net core 在iis上运行抛出502.5异常的部分原因以及解决方案 环境说明 已安装 .net core runtime 2.1.401 已安装 .net core windows ho ...

  4. 调用远程主机上的 RMI 服务时抛出 java.rmi.ConnectException: Connection refused to host: 127.0.0.1 异常原因及解决方案

    最近使用 jmx 遇到一个问题,client/server 同在一台机器上,jmx client能够成功连接 server,如果把 server 移植到另一台机器上192.168.134.128,抛出 ...

  5. 案例复现,带你分析Priority Blocking Queue比较器异常导致的NPE问题

    摘要:本文通过完整的案例复现来演示在什么情况会触发该问题,同时给出了处理建议.希望读者在编程时加以借鉴,避免再次遇到此类问题. 本文分享自华为云社区<Priority Blocking Queu ...

  6. Asp.net Core dotnet 发布类库文件 带上注释,发布预发行版,带上所有引用

    带上注释 效果图 带上所有引用 效果图 预发行版 效果图 由于微软取消了  project.json  这个json 转而用了csproj 用于保存配置 所以懵逼很大一会 资料来源 project.j ...

  7. 【HDU 4940】Destroy Transportation system(无源无汇带上下界可行流)

    Description Tom is a commander, his task is destroying his enemy’s transportation system. Let’s repr ...

  8. .NetCore HttpClient发送请求的时候为什么自动带上了一个RequestId头部?

    奇怪的问题 最近在公司有个系统需要调用第三方的一个webservice.本来调用一个下很简单的事情,使用HttpClient构造一个SOAP请求发送出去拿到XML解析就是了. 可奇怪的是我们的请求在运 ...

  9. 记得ajax中要带上AntiForgeryToken防止CSRF攻击

    经常看到在项目中ajax post数据到服务器不加防伪标记,造成CSRF攻击 在Asp.net Mvc里加入防伪标记很简单在表单中加入Html.AntiForgeryToken()即可. Html.A ...

  10. ZOJ 2314 带上下界的可行流

    对于无源汇问题,方法有两种. 1 从边的角度来处理. 新建超级源汇, 对于每一条有下界的边,x->y, 建立有向边 超级源->y ,容量为x->y下界,建立有向边 x-> 超级 ...

随机推荐

  1. 记录--Loading 用户体验 - 加载时避免闪烁

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 在切换详情页中有这么一个场景,点击上一条,会显示上一条的详情页,同理,点击下一条,会显示下一条的详情页. 伪代码如下所示: 我们定义了一个 ...

  2. 看看谷歌如何在目标检测任务使用预训练权值 | CVPR 2022

    论文提出能够适配硬件加速的动态网络DS-Net,通过提出的double-headed动态门控来实现动态路由.基于论文提出的高性能网络设计和IEB.SGS训练策略,仅用1/2-1/4的计算量就能达到静态 ...

  3. 郑州IT微信交流群期待你的加入

    我建了一个郑州IT微信交流群,找工作,找项目,人员招聘的都可以加入. 你有一个苹果,我有一个香蕉,合在一起,我们每个人都可以吃到两种水果了.广结人缘,扩大自己的人脉. 可以加我个人微信,拉你进群.

  4. 并发框架 LMAX Disruptor

    Introduction   Michael Barker edited this page on 2 Mar 2015 · 8 revisions The best way to understan ...

  5. Mybatis-Plus框架的BaseMapper的insert方法和自定义mapper里面的insert方法会用哪个

    在Mybatis-Plus框架中,当一个接口继承了BaseMapper接口,并且绑定了一个xml文件时,如果这个接口和xml文件中都定义了相同的方法,那么在调用该方法时,会优先调用xml文件中的方法, ...

  6. 数据解析之Beautifulsoup

    一.BeautifulSoup的简单使用 简单来说,Beautiful Soup是python的一个库,最主要的功能是从网页抓取数据.官方解释如下: Beautiful Soup提供一些简单的.pyt ...

  7. 7 JavaScript循环语句

    7 循环语句 在js中有三种循环语句. 首先是while循环. 它的逻辑和咱们python中的while几乎一模一样, 就是符号上有些许的区别. // 语法 while(条件){ 循环体 -> ...

  8. #虚树,树形dp#洛谷 4103 [HEOI2014]大工程

    题目 分析 建一棵虚树,然后树形dp,维护最长/短链和次长/短链, 对于第一个就是统计每条边有多少个点对经过就可以了 代码 #include <cstdio> #include <c ...

  9. synchronized解锁源码分析

    上篇花了很大篇幅写了synchronized的加锁流程,并对比了ReentrantLock的设计,这篇我们收个尾,来聊一聊解锁流程,本来准备一章解决的,写着写着觉得内容过多,其实上一篇和Reentra ...

  10. 如何通过 kubectl 进入 node shell

    概述 假设这样一个场景: 生产环境中,Node 都需要通过堡垒机登录,但是 kubectl 是可以直接在个人电脑上登录的. 这种场景下,我想要通过 kubectl 登录到 K8S 集群里的 Node, ...