.NET 纯原生实现 Cron 定时任务执行,未依赖第三方组件 (Timer 优化版)
在上个月写过一篇 .NET 纯原生实现 Cron 定时任务执行,未依赖第三方组件 的文章,当时 CronSchedule 的实现是使用了,每个服务都独立进入到一个 while 循环中,进行定期扫描是否到了执行时间来实现的,但是那个逻辑有些问题,经过各位朋友的测试,发现当多个任务的时候存在一定概率不按照计划执行的情况。

感谢各位朋友的积极淘汰,多交流一起进步。之前那个 while 循环的逻辑每循环一次 Task.Delay 1000 毫秒,无限循环,多个任务的时候还会同时有多个循环任务,确实不够好。
所以决定重构 CronSchedule 的实现,采用全局使用一个 Timer 的形式,每隔 1秒钟扫描一次任务队列看看是否有需要执行的任务,主要调整了 CronSchedule.cs
using Common;
using System.Reflection; namespace TaskService.Libraries
{
public class CronSchedule
{
private static List<ScheduleInfo> scheduleList = new();
private static Timer mainTimer; public static void Builder(object context)
{
var taskList = context.GetType().GetMethods().Where(t => t.GetCustomAttributes(typeof(CronScheduleAttribute), false).Length > 0).ToList(); foreach (var action in taskList)
{
string cron = action.CustomAttributes.Where(t => t.AttributeType == typeof(CronScheduleAttribute)).FirstOrDefault()!.NamedArguments.Where(t => t.MemberName == "Cron" && t.TypedValue.Value != null).Select(t => t.TypedValue.Value!.ToString()).FirstOrDefault()!; scheduleList.Add(new ScheduleInfo
{
CronExpression = cron,
Action = action,
Context = context
});
} if (mainTimer == default)
{
mainTimer = new(Run, null, 0, 1000);
}
} private static void Run(object? state)
{
var nowTime = DateTime.Parse(DateTimeOffset.UtcNow.ToString("yyyy-MM-dd HH:mm:ss")); foreach (var item in scheduleList)
{
if (item.LastTime != null)
{
var nextTime = DateTime.Parse(CronHelper.GetNextOccurrence(item.CronExpression, item.LastTime.Value).ToString("yyyy-MM-dd HH:mm:ss")); if (nextTime == nowTime)
{
item.LastTime = DateTimeOffset.Now; _ = Task.Run(() =>
{
item.Action.Invoke(item.Context, null);
});
}
}
else
{
item.LastTime = DateTimeOffset.Now.AddSeconds(5);
}
}
} private class ScheduleInfo
{
public string CronExpression { get; set; } public MethodInfo Action { get; set; } public object Context { get; set; } public DateTimeOffset? LastTime { get; set; }
}
} [AttributeUsage(AttributeTargets.Method)]
public class CronScheduleAttribute : Attribute
{
public string Cron { get; set; }
} }
这里的逻辑改为了注入任务时将 mainTimer 实例化启动,每一秒钟执行1次 Run方法,Run 方法内部用于 循环检测 scheduleList 中的任务,如果时间符合,则启动一个 Task 去执行对应的 Action,这样全局不管注册多少个服务,也只有一个 Timer 在循环运行,相对之前的 CronSchedule 实现相对更好一点。
使用的时候方法基本没怎么改,只是调整了CronSchedule.Builder 的调用 代码如下:
using DistributedLock;
using Repository.Database;
using TaskService.Libraries; namespace TaskService.Tasks
{
public class DemoTask : BackgroundService
{ private readonly IServiceProvider serviceProvider;
private readonly ILogger logger; public DemoTask(IServiceProvider serviceProvider, ILogger<DemoTask> logger)
{
this.serviceProvider = serviceProvider;
this.logger = logger;
} protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
CronSchedule.Builder(this); await Task.Delay(-1, stoppingToken);
} [CronSchedule(Cron = "0/1 * * * * ?")]
public void ClearLog()
{
try
{
using var scope = serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>(); //省略业务代码
Console.WriteLine("ClearLog:" + DateTime.Now);
}
catch (Exception ex)
{
logger.LogError(ex, "DemoTask.ClearLog");
}
} [CronSchedule(Cron = "0/5 * * * * ?")]
public void ClearCache()
{
try
{
using var scope = serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
var distLock = scope.ServiceProvider.GetRequiredService<IDistributedLock>(); //省略业务代码
Console.WriteLine("ClearCache:" + DateTime.Now);
}
catch (Exception ex)
{
logger.LogError(ex, "DemoTask.ClearCache");
}
} }
}
然后启动我们的项目就可以看到如下的运行效果:

最上面连着两个 16:25:53 并不是重复调用了,只是因为这个任务配置的是 1秒钟执行1次,第一次启动任务的时候执行的较为耗时,导致第一次执行和第二次执行进入到方法中的时间差太短了,这个只在第一次产生,对后续的执行计划没有影响。
.NET 纯原生实现 Cron 定时任务执行,未依赖第三方组件 (Timer 优化版)的更多相关文章
- .NET 纯原生实现 Cron 定时任务执行,未依赖第三方组件
常用的定时任务组件有 Quartz.Net 和 Hangfire 两种,这两种是使用人数比较多的定时任务组件,个人以前也是使用的 Hangfire ,慢慢的发现自己想要的其实只是一个能够根据 Cron ...
- .NET定时任务执行管理器开源组件–FluentScheduler
在日常项目里通常会遇到定时执行任务的需求,也就是定时器..NET Framework里关于定时器的类有3个,分别是System.Windows.Forms.Timer.System.Timers.Ti ...
- linux ,cron定时任务 备份mysql数据库
cron 定时任务执行备份脚本文件 backup.sh #!/bin/bash USER="root" PASSWORD="xxxxx" DATABASE=&q ...
- Go cron定时任务的用法
cron是什么 cron的意思就是:计划任务,说白了就是定时任务.我和系统约个时间,你在几点几分几秒或者每隔几分钟跑一个任务(job),就那么简单. cron表达式 cron表达式是一个好东西,这个东 ...
- spring cron 定时任务
文章首发于个人博客:https://yeyouluo.github.io 0 预备知识:cron表达式 见 <5 参考>一节. 1 环境 eclipse mars2 + Maven3.3. ...
- Spring task定时任务执行一段时间后莫名其妙停止的问题
前因: 我写了一个小项目,主要功能是用Spring task定时任务每天定时给用户发送邮件.执行了几个月一直没有问题,前几天,莫名其妙的突然不再发送邮件了. 只好花费一些时间来查看到底是什么原因造成的 ...
- cron定时任务介绍
什么是cron? Cron是linux系统中用来定期执行或指定程序任务的一种服务或软件.与它相关的有两个工具:crond 和 crontab.crond 就是 cron 在系统内的宿主程序,cront ...
- spring task 实现定时执行(补充:解决定时任务执行2次问题)
首先在spring-mvc.xml配置头文件引入: xmlns:task="http://www.springframework.org/schema/task" 其次引入task ...
- linux之cron定时任务介绍
前言 linux系统有一个专门用来管理定时任务的进程cron,一般是设置成开机自启动的,通过添加任务可以让服务器定时执行某些任务. cron介绍 linux系统有一个专门用来管理定时任务的进程cron ...
随机推荐
- CesiumJS 2022^ 源码解读[0] - 文章目录与源码工程结构
很高兴你能在浮躁的年代里还有兴趣阅读源代码,CesiumJS 至今已有十年以上,代码量也积累了三十多万行(未压缩状态). 我也很荣幸自己的文章能被读者看到,如果对你有帮助.有启发,点个赞就是对我最大的 ...
- CentOS7使用LVM缩减/home空间,扩大/空间
CentOS7使用LVM缩减/home空间,扩大/空间方法:把/home里的内容备份,然后将/home文件系统所在的逻辑卷删除,扩大/文件系统.新建/home,恢复/home的原内容1.查看默认分区[ ...
- Overfitting & Train Set & Test Set
假设数据集是独立同分布的,可以将数据集划分为不同的比例:Train Set and Test Set. 同时在Train Set and Test Set上做精度测试,或者隔一段时间在Test Set ...
- Thread类的常用方法_获取线程名称的方法和设置线程名称的方法
Thread类的常用方法 获取线程的名称: 1.使用Thread类中的方法getName() String getName() 返回该线程的名称 2.可以先获取到当前正在执行的线程,使用线程中的方法g ...
- ajax01_基础知识
ajax请求 ajax概述 组成:asynchronous + javascript + xml 特点: 可发送异步请求(这也是与传统请求方式的区别) 用javascript语言编写代码 前后端数据交 ...
- NOI / 2.1基本算法之枚举题解-1(3861字)制作不易
目录 1.15 Counterfeit Dollarhttp://noi.openjudge.cn/ch0201/15/ 2.1749 数字方格
- 2511-Druid监控功能的深入使用与配置-如何记录监控数据(基于logback)
Druid的监控很强大,但可惜的是监控数据是存在内存中的,需求就是定时把监控数据记录下来,以日志文件的形式或者数据库入库. 记录两种方式: 数据库入库 logback形式记录 原理(重点) 如果仅仅想 ...
- 并发刺客(False Sharing)——并发程序的隐藏杀手
并发刺客(False Sharing)--并发程序的隐藏杀手 前言 前段时间在各种社交平台"雪糕刺客"这个词比较火,简单的来说就是雪糕的价格非常高!其实在并发程序当中也有一个刺客, ...
- 学习javascript知识
开始学习了 努力----努力----努力 从今天开始 绝不 三天打鱼两天晒网 先把基础再巩固一下
- Apache SeaTunnel (Incubating) 2.1.0 发布,内核重构、全面支持 Flink
2021 年 12 月 9 日,SeaTunnel (原名 Waterdrop) 成功加入 Apache 孵化器,进入孵化器后,SeaTunnel 社区花费了大量时间来梳理整个项目的外部依赖以确保整个 ...