C#执行任务Task,如果任务超时的话就取消当前任务,重新执行,规定次数内执行成功则执行成功的委托,超过规定的次数则失败的委托

在上位机和MES通信,尤其是上位机向MES发送数据的的时候,经常需要设置一个超时处理的时间和重复执行的次数。例如MES的超时响应事件是5秒,超过5秒的话,上位机重新访问MES一次,连续三次都超时的话,把要上传的数据缓存到本地。

关于上位机和MES系统的通讯方式,有的工厂是WebService(数据格式有JSON和XML之分),有的工厂是Socket,有的工厂是自己封装的库,所以写了段儿程序执行任务的代码,通讯方式如何都可以写到TASK中

        /// <summary>
        /// 执行任务超时的话,取消当前执行的任务,重新执行任务。
        /// 在最大重新执行次数内执行完,则执行onSuccess委托
        /// 执行次数达到最大执行次数,任务还是超时的话,则执行onFailure委托
        /// </summary>
        /// <param name="operation">要执行的任务</param>
        /// <param name="maxRetryCount">最大重复执行的次数</param>
        /// <param name="timeout">单次执行任务超时的时间</param>
        /// <param name="onSuccess">在最大重新执行次数内执行完,执行的委托</param>
        /// <param name="onFailure">执行次数达到最大执行次数,任务还是超时的话,执行的委托        </param>
        /// <returns></returns>
        public static async Task ExecuteWithRetriesAsync(
            Func<Task> operation,
            int maxRetryCount,
            TimeSpan timeout,
            Func<Task> onSuccess,
            Func<Task> onFailure)
        {

            while (true)
            {
                if (maxRetryCount == 0)
                {
                    // 超过最大重试次数,执行失败委托并退出函数
                    await onFailure(); //失败该走的事情,比如把数据缓存到本地
                    return;
                }
                var task = operation();
                if (await Task.WhenAny(task, Task.Delay(timeout)) == task)
                {
                    try
                    {
                        // 如果成功,执行成功委托并退出函数
                        await onSuccess();
                        return;
                    }
                    catch (OperationCanceledException ex)
                    {
                        Console.WriteLine($"任务超时取消, message: {ex.Message}" + " " + DateTime.Now);
                    }
                }
                else
                {
                    // 超时,取消任务
                    await CancelTaskAsync(task);
                }
                Console.WriteLine($"任务超时,重试任务, retry count: {maxRetryCount}");
                maxRetryCount--;
            }
        }

        /// <summary>
        /// 取消任务
        /// 在 CancelTaskAsync 方法中,不应该释放 CancellationTokenSource 对象,它的责任只是调用任务并稍后取消它(用于超时)。
        /// 在正常情况下,CancellationTokenSource对象将由垃圾回收器在不再使用它时自动释放。因此,在 CancelTaskAsync 中不需要对 cts 对象进行显式释放。
        /// </summary>
        /// <param name="task"></param>
        /// <returns></returns>
        public static async Task CancelTaskAsync(Task task)
        {
            var cts = new CancellationTokenSource();
            try
            {
                cts.Cancel();
                await task;
            }
            catch (OperationCanceledException ex)
            {
                Console.WriteLine($"任务取消CancelTaskAsync, message: {ex.Message}");
            }
        }

        /// <summary>
        /// 模拟一个需要很长时间才能执行完毕的任务
        /// </summary>
        /// <param name="timeOutSec"></param>
        /// <returns></returns>
        public static async Task LongRunningOperationAsync(int timeOutSec)
        {
            using (var tokenSource = new CancellationTokenSource())
            {
                Console.WriteLine("");
                // 确保在超时后取消任务
                tokenSource.CancelAfter(TimeSpan.FromSeconds(timeOutSec));

                // 在任务执行期间定期检查 CancellationToken,
                // 假设当前任务要执行8秒
                await Task.Delay(TimeSpan.FromSeconds(8), tokenSource.Token);

                Console.WriteLine("****Operation succeeded.");
            }
        }

注意上面这段代码使用了 Task.WhenAny 方法,该方法接受一个 Task 对象数组,并返回一个 Task<Task> 对象,该对象的结果是完成最先完成的 Task 对象。使用 await 运算符等待 Task.WhenAny 方法返回的 Task,这将导致代码在这里等待,直到指定的任务 task 或超时任务 Task.Delay 完成为止。 如果最先完成的任务是指定的任务 task,则 Task.WhenAny 的返回结果即为 task,这意味着 await Task.WhenAny(task, Task.Delay(timeout)) 将返回 task 对象的结果,此时,if 语句的条件判断结果为 True,进入 if 块内部的代码。否则,如果超时任务先完成,Task.WhenAny 的返回结果将是超时任务 Task.Delay,在这种情况下,if 语句将不会执行,代码将继续执行 Task.Delay 后面的语句。 因此,这个 if 语句是用来确定任务是否在规定的超时时间内完成的。如果完成,在 if 块内部执行相应的代码;如果没有,在超时后执行某种其他的操作。

模拟条件执行任务,模拟超时的时间为5秒,最多执行任务3次。

public static async Task DoSomethingAgainAndAgain()
        {
            // 累计重试和最大重试次数初始化

            int maxRetryCount = 3;//最多执行任务的次数
            int timeOutSec = 5;//每次执行任务的超时时间

            // 成功和失败的委托初始化
            Func<Task> onSuccess = () => Task.FromResult<object>("do nothing");
            Func<Task> onFailure = () => Task.FromResult<object>("do nothing");

            // 执行异步任务并在超时后重试最大重试次数
            await ExecuteWithRetriesAsync(
                async () => await LongRunningOperationAsync(timeOutSec),
                maxRetryCount,
                TimeSpan.FromSeconds(timeOutSec),
                onSuccess, onFailure);
        }

也可以用下面这种方法来模拟任务

static async Task LongRunningOperationAsync2(int timeOutSec)
        {
            using (var tokenSource = new CancellationTokenSource())
            {
                var startTime = DateTime.Now;
                // 在任务执行期间定期检查 CancellationToken,
                // 如果它已取消,可以安全地退出。
                while (true)
                {
                    if ((DateTime.Now - startTime) >= TimeSpan.FromSeconds(timeOutSec))
                    {
                        tokenSource.Cancel();
                    }
                    if (tokenSource.IsCancellationRequested)
                    {
                        return;
                    }
                    await Task.Delay(TimeSpan.FromSeconds(1));
                }
            }
        }

编写上述代码用了如下知识点,这些都很重要,希望大家好好学习,天天想上

  1. 异步编程 - ExecuteWithRetriesAsync 方法的定义和实现都是基于异步编程模型的。

  2. 委托和Lambda表达式 - 函数 ExecuteWithRetriesAsync 接受两个参数类型为 Func<Task> 的委托参数,在函数体内被直接调用,使用了 Lambda 表达式来定义这些委托。

  3. 异常处理 - 为了捕获可能在异步执行过程中产生的异常,代码块使用了 try/catch 语句。

  4. Task 和 Task.Delay - Task 类型用于表示异步操作的进程,而 Task.Delay 方法用于异步延迟一段时间后执行一个操作。

  5. Task.WhenAny - Task.WhenAny 方法用于等待任何一个任务完成,如果第一个任务完成,则返回该任务,如果没有完成,则返回一个默认的任务。在这个方法中,使用 Task.WhenAny 方法来判断当前任务是否已经超时。

  6. async/await 关键字 - 使用了 async/await 关键字以帮助简化对异步方法的调用

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我理解了你的问题。你想知道如何在C# Furion中指定一个特定的时间来执行任务。 在C# Furion中,你可以使用.NET Framework中的`System.Timers.Timer`类来实现定时任务。你可以创建一个`Timer`对象,并设置它的`Interval`属性为`0`,然后在`Elapsed`事件中检查当前时间是否等于或超过指定的时间,如果是,则执行任务。 以下是一个示例代码,展示如何在C# Furion中指定时间执行任务: ``` csharp using System; using System.Timers; class Program { static void Main() { // 创建一个 Timer 对象 Timer timer = new Timer(); // 设置 Timer 的间隔为 0 毫秒 timer.Interval = 0; // 设置 Timer 的 AutoReset 属性为 false,以便 Timer 只触发一次 timer.AutoReset = false; // 设置要执行任务的时间 DateTime taskTime = new DateTime(2022, 12, 31, 23, 59, 59); // 添加 Timer 的 Elapsed 事件处理程序 timer.Elapsed += (sender, e) => { // 检查当前时间是否等于或超过任务时间 if (DateTime.Now >= taskTime) { // 到达任务时间,执行任务 Console.WriteLine("任务已经开始执行!"); } else { // 如果当前时间还没到达任务时间,重新启动 Timer timer.Start(); } }; // 启动 Timer timer.Start(); // 阻塞主线程,以便 Timer 有足够时间触发 Elapsed 事件 Console.ReadLine(); } } ``` 在这个示例代码中,我们首先创建了一个`Timer`对象,并将它的`Interval`属性设置为`0`,表示让`Timer`立即触发`Elapsed`事件。然后,我们设置了要执行任务的时间,并添加了`Elapsed`事件的处理程序。在`Elapsed`事件中,我们检查当前时间是否等于或超过指定的时间。如果是,我们就执行任务。如果还没有到达任务时间,我们就重新启动`Timer`,以便在到达任务时间时再次触发`Elapsed`事件。 当然,这只是一个简单的示例,你可以根据你的具体需求来修改代码。但这个示例将会给你提供一个基本的思路,来在C# Furion中指定时间执行任务

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值