前言:

刷帖子看到一篇 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. Codeforces 1113C: Sasha and a Bit of Relax(位运算|异或)

    time limit per test: 1 secondmemory limit per test: 256 megabytesinput: standard inputoutput: standa ...

  2. HITCON 2019 Lost Modular again writeup

    HITCON 2019 Lost Modular again writeup 算是基础题,有很多之前题的影子,做不出来纯属菜. 题目 加密脚本 from Crypto.Util.number impo ...

  3. Improving Adversarial Robustness Using Proxy Distributions

    目录 概 主要内容 proxy distribution 如何利用构造的数据 Sehwag V., Mahloujifar S., Handina T., Dai S., Xiang C., Chia ...

  4. Simplicial principal component analysis for density functions in Bayes spaces

    目录 问题 上的PCA Hron K, Menafoglio A, Templ M, et al. Simplicial principal component analysis for densit ...

  5. C++模拟python风格的print函数--打印vector,map,list等结构

    // 最基本实现 template<typename T> static void print(T t) { std::cout << t; } // 处理 std::pair ...

  6. Vue.js高效前端开发知识 • 【目录】

    持续更新中- 章节 内容 实践练习 Vue.js高效前端开发 • (实践练习) 第1章 Vue.js高效前端开发 • [ 一.初识Vue.js ] 第2章 Vue.js高效前端开发 • [ 二.Vue ...

  7. Java面向对象笔记 • 【第8章 内部类和泛型】

    全部章节   >>>> 本章目录 8.1 内部类 8.1.1 内部类概述 8.1.2 内部类使用 8.1.3 实践练习 8.2 静态内部类 8.2.1 静态内部类的实现 8.2 ...

  8. CentOS7.6下安装Redis5.0.7

    此次安装是在CentOS7下安装Redis5.0.7 一.首先准备Redis安装包 这里下载的是 redis-5.0.7.tar.gz 安装包,并将其直接放在了 root ⽬录下 压缩包下载地址:ht ...

  9. JDK线程池异常处理方式

    1. 抛出异常 execute() java.util.concurrent.ThreadPoolExecutor#runWorker 中抛出,抛出之后经过以下两个步骤: catch块捕获,捕获之后再 ...

  10. java单元测试调用mybatis接口并执行

    今天想使用单元测试类,存储一些数据到mysql,可是,一直在报错,org.springframework.beans.factory.NoSuchBeanDefinitionException: No ...