前言:

刷帖子看到一篇 Go 记录一次groutine通信与context控制 看了一下需求背景,挺有意思的,琢磨了下.net core下的实现

需求背景:

项目中需要定期执行任务A来做一些辅助的工作,A的执行需要在超时时间内完成,如果本次执行超时了,那就不对本次的执行结果进行处理(即放弃这次执行)。同时A又依赖B,C两个子任务的执行结果。B, C之间相互独立,可以并行的执行。但无论B,C哪一个执行失败或超时都会导致本次任务执行失败。

需求提炼:

  • A任务必须在指定时间内完成,否则任务失败
  • A任务依赖B,C任务,B,C可以并行,任何一个失败,则A任务失败
  • A任务在超时时间内,是否需求记录子任务执行详情(根据业务需求来定)

.net里设置超时的 Task

public static class TaskHelper
{ // 有返回值
public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeout)
{
using (var timeoutCancellationTokenSource = new CancellationTokenSource())
{
var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token));
if (completedTask == task)
{
timeoutCancellationTokenSource.Cancel();
return await task; // Very important in order to propagate exceptions
}
else
{
throw new TimeoutException("The operation has timed out.");
}
}
} // 无返回值
public static async Task TimeoutAfter(this Task task, TimeSpan timeout)
{
using (var timeoutCancellationTokenSource = new CancellationTokenSource())
{
var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token));
if (completedTask == task)
{
timeoutCancellationTokenSource.Cancel();
await task; // Very important in order to propagate exceptions
}
else
{
throw new TimeoutException("The operation has timed out.");
}
}
}
}

这里参考资料,写了个拓展方法,主要用到CancellationTokenSource 与 Task.WhenAny

可以参考 C#中CancellationToken和CancellationTokenSource用法

可以参考 Task.WhenAny 方法

这里需要特别注意,在异步操作里,如果异步已经执行了,再执行取消时无效的,这是就需要我们自己在异步委托中检测了

知道了这两个函数的作用,这段代码就很好理解了,通过Task.WhenAny返回最先完成的任务,如果是业务任务先完成,则调用timeoutCancellationTokenSource.Cancel()终止超时任务,等待业务任务结果,反之则直接抛出timeout异常

测试代码

    [TestMethod]
public async Task TestMethod1()
{
//A 任务必须在指定时间内完成,否则任务失败
//A 任务依赖B,C任务,B,C可以并行,任何一个失败,则A任务失败
//A任务
try
{
//有效时间3s
var timeOut = TimeSpan.FromSeconds(3);
await Task.Run(async () =>
{
List<Task<(string, bool)>> tasks = new List<Task<(string, bool)>>();
//B任务
tasks.Add(Task.Run(async () =>
{
return ("B", await TestTask("B"));
}).TimeoutAfter(timeOut)); //C任务
tasks.Add(Task.Run(async () =>
{
return ("C", await TestTask("C"));
}).TimeoutAfter(timeOut)); var res = await Task.WhenAll(tasks); //两个任务,任何一个失败,则A任务失败
foreach (var item in res)
{
Console.WriteLine(item);
} }).TimeoutAfter(timeOut); }
catch (Exception ex)
{
Console.WriteLine("A任务执行超时了");
} //await Task.Delay(3000); } public async Task<bool> TestTask(string name)
{
var startTime = DateTime.Now;
Console.WriteLine($"{startTime}---->{name}任务开始执行");
//随机堵塞1-5s
var t = new Random().Next(1, 5);
await Task.Delay(t * 1000); var endTime = DateTime.Now; ;
var time = (endTime - startTime).TotalSeconds; //随机数,模拟业务是否成功
var res = new Random().Next(1, 10);
Console.WriteLine($"{endTime}---->{name}任务执行完毕,耗时{time} s");
return res <= 7;
}

测试截图

搞定收工

故事在这里就结束了吗? 显然没有,这么简单也没必要水一篇博客了

我们能做到在3s内响应结果,也算基本上满足了需求,那超时的子任务,是否会继续执行呢?

仔细看代码,就算超时,也是停止的Task.Delay() 这个线程,与业务线程没有半毛钱关系,那业务线程肯定会继续执行

眼尖的同学已经看到最后一张图,B任务执行了3.0076088s,按道理B任务是已经超时了,这段话是不会输出的,那如果我让主线程晚点退出,那超时的子线程是否能正常执行, //await Task.Delay(3000); 将这段代码取消注释,再来观看结果

有没有一种被欺骗的感觉,写了一个假的超时时间,哈哈哈哈.....

这里需要特别注意,在异步操作里,如果异步已经执行了,再执行取消时无效的,这是就需要我们自己在异步委托中检测了

如果写了一个死循环的task,那后果将不堪设想,这个时候,就需要慎重了,在使用多线程取消令牌的时候,除了需要执行Cancel()方法,还需要在子任务内自己捕获CancellationTokenSource.Token.ThrowIfCancellationRequested()

记一次.net core 异步线程设置超时时间的更多相关文章

  1. FFmpeg命令读取RTMP流如何设置超时时间

    子标题:FFmpeg命令录制RTMP流为FLV文件时如何设置超时时间 | FFmpeg命令如何解决录制产生阻塞的问题0x001: 前言 今天在测试程序时遇到两个问题.Q1:ffmpeg录制RTMP流并 ...

  2. C# UdpClient 设置超时时间

    /********************************************************************** * C# UdpClient 设置超时时间 * 说明: ...

  3. mongodb3.6 (五)net 客户端访问mongodb设置超时时间踩过的“坑”

    前言 在上一篇文章中,我们有提到net访问mongodb连接超时默认为30秒,这个时间在实际项目中肯定是太长的.而MongoClientSettings 也确是提供了超时属性,如下图: 可实际使用中, ...

  4. Go基础系列:为select设置超时时间

    Go channel系列: channel入门 为select设置超时时间 nil channel用法示例 双层channel用法示例 指定goroutine的执行顺序 After() 谁也无法保证某 ...

  5. GuzzleHttp 请求设置超时时间

    之前调用一个三方的 WEB API,大量的请求超时,导致 PHP 进程被占用完.整个网站一直报 504. 其中一个优化措施就是对三方 API 调用设置超时时间. use GuzzleHttp\Clie ...

  6. Mybatis设置超时时间

    Mybatis设置超时时间 mybatis如果不指定,默认超时时间是不做限制的,默认值为0.mybatis sql配置超时时间有两种方法: 1.全局配置 在mybatis配置文件的settings节点 ...

  7. winform设置超时时间

    ); //设置超时时间 var completedTask = await Task.WhenAny(new Task(async () => { );//执行的方法示例这里用延迟代替 }), ...

  8. HttpClient 如何设置超时时间

    今天分享一个巨坑,就是 HttpClient.这玩意有多坑呢?就是每个版本都变,近日笔者深受其害. 先看一下代码,我要发送请求调用一个c++接口. public static String doPos ...

  9. 爬虫学习笔记之为什么要设置超时时间,怎么设置(使用selenium)

    一个程序没有设置超时时间,就可以说是一段有缺陷的代码. 读取超时指的就是客户端等待服务器发送请求的时间.(特定地,它指的是客户端要等待服务器发送字节之间的时间.在 99.9% 的情况下这指的是服务器发 ...

随机推荐

  1. PLSQL到期处理

    一.输入指令"regedit"打开注册表 二.指令输入完毕后,按回车键,会进入这个界面. 三.注册表里按HKEY_CURRENT_USER\Software\Allround Au ...

  2. Jenkins安装教程:Windows环境通过jenkins.war安装

    1.Windows操作系统下,安装jdk.tomcat.maven.git,并配置好对应的环境变量,安装教程请自行查询资料 2.将下载的jenkins.war放入到tomcat的webapp文件夹下, ...

  3. Java初学者作业——添加程序断点,以Debug模式运行程序,观察变量的交换

    返回本章节 返回作业目录 需求说明: 定义两个变量,分别用于存储学生的语文成绩和数学成绩,通过程序将这两个变量的值进行交换,输出变量之前的值和交换后变量中的值. 添加断点,观察变量值的交换过程. 实现 ...

  4. Hive on Spark和Spark sql on Hive,你能分的清楚么

    摘要:结构上Hive On Spark和SparkSQL都是一个翻译层,把一个SQL翻译成分布式可执行的Spark程序. 本文分享自华为云社区<Hive on Spark和Spark sql o ...

  5. .net core获取本地Ip地址的方法

    笔记: /// <summary> /// 获取本地Ip地址 /// </summary> /// <returns></returns> public ...

  6. javascript中逻辑运算(||,&&,!)

    作为一个后端开发的程序员,一直就对JavaScript情有独钟,作为一门前后端通吃的语言,必须赞一下.而且之前很长一段时间都有在做JavaScript,一路都是和ie8死磕,磕完又找低版本的谷歌磕,坑 ...

  7. Linux_yum安装时报404错误

    使用yum安装报错如下: [root@localhost ~]# yum install gcc 已加载插件:fastestmirror Loading mirror speeds from cach ...

  8. Linux shell 脚本中使用 alias 定义的别名

    https://www.cnblogs.com/chenjo/p/11145021.html 核心知识点: 用 shopt 开启和关闭 alias 扩展 交互模式下alias 扩展默认是开启的,脚本模 ...

  9. 第10组 Beta冲刺 (2/5)

    1.1基本情况 ·队名:今晚不睡觉 ·组长博客:https://www.cnblogs.com/cpandbb/p/14015412.html ·作业博客:https://edu.cnblogs.co ...

  10. Go语言中各种数据格式转换

    Go语言各种数据类型格式转换 package main import ( "encoding/json" "fmt" "reflect" & ...