一次.net core异步线程设置超时时间的实战记录

目录
  • 前言:
  • 需求背景:
  • 需求提炼:
  • .net里设置超时的 Task
  • 测试代码
  • 测试截图
  • 搞定收工
  • 总结

前言:

刷帖子看到一篇 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异步线程设置超时时间的文章就介绍到这了,更多相关.net core异步线程设置超时时间内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • .net core并发下线程安全问题详解

    抱歉,其实内容并不如题!!! 背景(写测试demo所出现的异常,供大家学习与拍砖): .net core webapi项目,做了一个授权的filter(真正的生产项目的话,JWT很棒),单个接口测试没有问题,当用前端在同一个页面调用多个接口的时候,运行服务,打开页面,然后--Exceptions--(真正的开发中大家应该也会遇到) 异常1:An attempt was made to use the context while it is being configured. A DbContex

  • 一次.net core异步线程设置超时时间的实战记录

    目录 前言: 需求背景: 需求提炼: .net里设置超时的 Task 测试代码 测试截图 搞定收工 总结 前言: 刷帖子看到一篇 Go 记录一次groutine通信与context控制 看了一下需求背景,挺有意思的,琢磨了下.net core下的实现 需求背景: 项目中需要定期执行任务A来做一些辅助的工作,A的执行需要在超时时间内完成,如果本次执行超时了,那就不对本次的执行结果进行处理(即放弃这次执行).同时A又依赖B,C两个子任务的执行结果.B, C之间相互独立,可以并行的执行.但无论B,C哪

  • Android 异步任务 设置 超时使用handler更新通知功能

    Android 使用AsyncTask设置请求超时的注意事项 final AsyncTaskTools task = new AsyncTaskTools(dialog, doTask, result, context); mTask.execute("蓝牙读卡"); new Thread() { public void run() { try { /** * 在这里你可以设置超时时间, * 切记:这段代码必须放到线程中执行,因为不放单独的线程中执行的话该方法会冻结UI线程 * 直接导

  • java httpclient设置超时时间和代理的方法

    设置超时时间 设置HttpClient的超时时间,非常有必要性,因为httpclient 默认超时时间很长,自己可以测试一下是多久,设置超时时间否则会影响自己系统的业务逻辑,例如阻塞系统,影响系统的吞吐量,占用线程数. httpclient 4.4版本之后将这些设置封装到 RequestConfig 对象里,其中 setConnectTimeout 是设置连接到目标 URL 的等待时长,超过这个时间还没连上就抛出连接超时: setConnectionRequestTimeout 是从connec

  • springcloud之Feign、ribbon如何设置超时时间和重试机制

    Feign.ribbon设置超时时间和重试机制 前言 我们在微服务调用服务的时候,会使用feign和ribbon,比如有一个实例发生了故障而该情况还没有被服务治理机制及时的发现和摘除,这时候客户端访问该节点的时候自然会失败. 所以,为了构建更为健壮的应用系统,我们希望当请求失败的时候能够有一定策略的重试机制,而不是直接返回失败. 先看一个配置: #预加载配置,默认为懒加载 ribbon: eager-load: enabled: true clients: zoo-plus-email zoo-

  • Springboot RestTemplate设置超时时间的简单方法

    目录 排查: Java Config配置 配置文件指定 RestTemplate 设置超时时间注意点 总结 RestTemplate未设置超时时间,导致RabbitMQ队列大量堆积,消费者假死,不进行消费,类似线程堵塞. 排查: 从日志排查问题,在从进入消费者到结束期间,会调用业务服务,然而通过开始时间至结束时间,花费的时间竟然高达100多秒,查看业务服务未发现异常.出现这种问题的原因也有可能在消费期间网络波动导致HTTP连接出现问题,一直处于等待状态.由此判断可能是HTTP超时时间未设置导致的

  • Redis分布式锁如何设置超时时间

    目录 Redis分布式锁设置超时时间 网络抖动 Redis宕机 Redis分布式锁的超时问题 Redis分布式锁设置超时时间 Redis分布式锁主要依靠Redis服务来完成,我们的应用程序其实是Redis节点的客户端,一旦客户端没有释放锁,服务端就会一直持有这个锁,其他进程中的线程就无法获取到这把锁,于是就会发生锁死的情况. 所以我们在使用Redis分布式锁的时候,务必要设置锁的过期时间. 主要基于下面两点: 网络抖动 客户端A中的一个线程获取到了锁,然后执行finally中的释放锁的代码时,这

  • PHP请求远程地址设置超时时间的解决方法

    php请求远程地址设置超时时间,主要讲解file_get_contents.fopen.curl这三个简单常用函数设置超时时间的方法,一般情况下建议使用curl,性能最好,效率也最高. 1.file_get_contents 请求超时设置 $timeout = array( 'http'=> array( 'timeout'=>5//设置一个超时时间,单位为秒 ) ); $ctx = stream_context_create($timeout); $text = file_get_conte

  • C++设置超时时间的简单实现方法

    本文实例讲述了C++设置超时时间的简单实现方法,代码简单易懂,功能实用.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: BOOL SetTimeOut(SOCKET s, int nTime, BOOL bRecv)  {      int ret = ::setsockopt(s, SOL_SOCKET, bRecv?SO_RCVTIMEO:SO_SNDTIMEO, (char*)nTime, sizeof(nTime));      return ret!=SOCKET_ER

  • 客户端设置超时时间真的很重要

    概述 在一条慢SQL导致购物车服务无法使用的解决方案一文中,提到了客户端调用购物车服务的时候,超时了.如果当时客户端没有设置超时时间的话,会在客户端中产生级联故障.先用一张图来说明一下. 聚合层除了调用购物车微服务,还调用了营销系统微服务.如果购物车服务的接口响应时间很慢,而客户端聚合层调用购物车服务时,又没有设置超时时间,那么将占有大量的连接,如果请求购物车服务的请求量比较大,瞬间就会把连接占用完,直接导致聚合层调用营销系统时,需要阻塞住等待获取连接,这样的话,整个小程序的很多功能就都用不了了

  • feign客户端设置超时时间操作

    我就废话不多说了,大家还是直接看代码吧~ /** * feign调用客户端 */ @FeignClient(name = "user", url = "${user.url}", configuration = MyFeignDecoder.class) public interface UserClient { @GetMapping("/rest/user/get/detail/{loginName}") JSONObject getUser

随机推荐