.Net之时间轮算法(终极版)定时任务

TimeWheelDemo

一个基于时间轮原理的定时任务

对时间轮的理解

其实我是有一篇文章(.Net 之时间轮算法(终极版)[1])针对时间轮的理论理解的,但是,我想,为啥我看完时间轮原理后,会采用这样的方式去实现。

可能只是一些小技巧不上大雅之堂吧,大佬看看就行了。

当然如果大佬有别的看法,也请不吝赐教,互相交流,一起进步。

项目是基于时间轮理解上的一个任务调度轻型框架

作用么,造个小轮子,顺便,对任务调度的实现多一些深度的思考和了解。

这个框架实现了啥子

实现了对方法的定时 循环执行。大概样子是下面这样的

TimeWheel timeWheel = new TimeWheel();timeWheel.AddTask(new Job("定时1", () => { Console.WriteLine($"定时每1秒 {DateTime.Now}"); }, new TimeTask(TimeSpan.FromSeconds(1))));timeWheel.AddTask(new Job("定时2", () => { Console.WriteLine($"定时2每10执行 {DateTime.Now}"); }, new TimeTask(TimeSpan.FromSeconds(10))));timeWheel.AddTask(new Job("CRON", () => { Console.WriteLine($"CRON 每5秒 {DateTime.Now}"); }, new CronTask("*/5 * * * * *")));timeWheel.AddTask(new Job("死信", () => { Console.WriteLine($"死信执行 {DateTime.Now}"); }, new DelayTask(TimeSpan.FromSeconds(20))));timeWheel.AddTask(new Job("死信1", () => { Console.WriteLine($"死信1执行 {DateTime.Now}"); }, new DelayTask(TimeSpan.FromSeconds(10))));timeWheel.Run();

能实现,定时任务,死信任务,能支持CRON表达式

定时任务如下 (TimeTask)

timeWheel.AddTask(new Job("定时1", () => { Console.WriteLine($"定时每1秒 {DateTime.Now}"); }, new TimeTask(TimeSpan.FromSeconds(1))));timeWheel.AddTask(new Job("定时2", () => { Console.WriteLine($"定时2每10执行 {DateTime.Now}"); }, new TimeTask(TimeSpan.FromSeconds(10))));

通过 TimeTask进行实现的

CRON定时任务 (CronTask)

主要是基于 NCrontab 库,实现对CRON表达式的解析。省的自己从头解析了

timeWheel.AddTask(new Job("CRON", () => { Console.WriteLine($"CRON 每5秒 {DateTime.Now}"); }, new CronTask("*/5 * * * * *")));

这样就能实现对特定任务的执行

死信任务,延迟任务 (DelayTask)

很多死信都是基于消息队列的,但是应该也有一些实际应用中的应用场景吧。看具体了。

timeWheel.AddTask(new Job("死信", () => { Console.WriteLine($"死信执行 {DateTime.Now}"); }, new DelayTask(TimeSpan.FromSeconds(20))));timeWheel.AddTask(new Job("死信1", () => { Console.WriteLine($"死信1执行 {DateTime.Now}"); }, new DelayTask(TimeSpan.FromSeconds(10))));

实现

能按照指定ID名,来实现对任务的移除

比如下边的,就能直接移除死信的任务。可以别的定时器执行了任务,然后,对此任务进行清除。

timeWheel.RemoveTask("死信");

基本上,只要没有被执行的任务,都会被取消执行的。

效果图

24e3a1a07fe01bc43f283fae071683b6.gif

代码详解

先看看main函数的示例

static void Main(string[] args)
{TimeWheel timeWheel = new TimeWheel();timeWheel.AddTask(new Job("定时1", () => { Console.WriteLine($"定时每1秒 {DateTime.Now}"); }, new TimeTask(TimeSpan.FromSeconds(1))));timeWheel.AddTask(new Job("定时2", () => { Console.WriteLine($"定时2每10执行 {DateTime.Now}"); }, new TimeTask(TimeSpan.FromSeconds(10))));timeWheel.AddTask(new Job("CRON", () => { Console.WriteLine($"CRON 每5秒 {DateTime.Now}"); }, new CronTask("*/5 * * * * *")));timeWheel.AddTask(new Job("死信", () => { Console.WriteLine($"死信执行 {DateTime.Now}"); }, new DelayTask(TimeSpan.FromSeconds(20))));timeWheel.AddTask(new Job("死信1", () => { Console.WriteLine($"死信1执行 {DateTime.Now}"); }, new DelayTask(TimeSpan.FromSeconds(10))));timeWheel.Run();Task.Run(() =>{Thread.Sleep(10 * 1000);timeWheel.RemoveTask("死信");Console.WriteLine("移除死信");Thread.Sleep(10 * 1000);timeWheel.RemoveTask("CRON");Console.WriteLine("移除任务CRON");});Console.WriteLine("开始运行时间轮!");Console.ReadLine();
}

时间调度

/// <summary>/// 时间调度方式/// </summary>public interface IScheduledTask{/// <summary>/// 获取下一个时间/// </summary>/// <returns></returns>public DateTime? GetNextTime();}

核心的时间轮

/// <summary>/// 时间轮算法(终极)实现/// 大部分都是支持秒级,所以,按照秒级进行实现/// 任务体得有它自己的任务唯一的ID/// </summary>public class TimeWheel{/// <summary>/// 时间调度列表/// </summary>private ConcurrentDictionary<long, HashSet<string>> TimeTasks { get; set; } = new();/// <summary>/// 任务列表/// </summary>private ConcurrentDictionary<string, IJob> ScheduledTasks { get; set; } = new();/// <summary>/// 是否运行中/// </summary>private bool isRuning = false;/// <summary>/// 运行核心/// </summary>public void Run(){isRuning = true;Task.Run(() =>{while (isRuning){var timeStamp = GenerateTimestamp(DateTime.Now);Task.Run(() => { Trigger(timeStamp); });var offset = 500 - DateTime.Now.Millisecond;SpinWait.SpinUntil(() => false, 1000 + offset);}});}public void Stop(){isRuning = false;}/// <summary>/// 定时触发器/// </summary>/// <param name="timeStamp"></param>private void Trigger(long timeStamp){var oldTimeStamp = timeStamp - 1;var list = TimeTasks.Keys.Where(t => t <= oldTimeStamp).ToList();foreach (var item in list){TimeTasks.TryRemove(item, out var _);}TimeTasks.TryGetValue(timeStamp, out var result);if (result?.Any() == true){var Now = DateTime.Now;foreach (var id in result){//找到指定的任务if (ScheduledTasks.TryGetValue(id, out IJob job)){Task.Run(() => { job.Execute(); });var NewTime = job.GetNextTime();if (NewTime.HasValue && NewTime >= Now){AddTask(NewTime.Value, id);}}}}}/// <summary>/// 添加任务/// </summary>/// <param name="dateTime"></param>/// <param name="scheduledTask"></param>private void AddTask(DateTime dateTime, string ID){var timeStamp = GenerateTimestamp(dateTime);TimeTasks.AddOrUpdate(timeStamp, new HashSet<string>() { ID }, (k, v) =>{v.Add(ID);return v;});}/// <summary>/// 增加一个任务/// </summary>public void AddTask(IJob job){if (ScheduledTasks.ContainsKey(job.ID)){throw new ArgumentException($"{nameof(job)} 参数 {nameof(job.ID)}重复!");}else{ScheduledTasks.TryAdd(job.ID, job);}var time = DateTime.Now;var NewTime = job.GetNextTime();if (NewTime.HasValue && NewTime >= time){Console.WriteLine($"新增任务:{job.ID}");AddTask(NewTime.Value, job.ID);}}/// <summary>/// 移除某个任务的Task/// </summary>/// <param name="ID"></param>public void RemoveTask(string ID){var ids = ScheduledTasks.Values.Where(t => t.ID == ID)?.Select(t => t.ID).ToList();if (ids?.Any() == true){foreach (var id in ids){if (ScheduledTasks.TryGetValue(id, out var job)){job.Cancel();ScheduledTasks.TryRemove(id, out _);}}}}/// <summary>/// 获取时间戳/// </summary>private long GenerateTimestamp(DateTime dateTime){return new DateTimeOffset(dateTime.ToUniversalTime()).ToUnixTimeSeconds();}}

任务体 (IJob)

/// <summary>/// 任务体/// </summary>public interface IJob{/// <summary>/// 任务ID,唯一/// </summary>/// <returns></returns>public string ID { get; }/// <summary>/// 脚本/// </summary>/// <returns></returns>public void Execute();/// <summary>/// 取消执行/// </summary>public void Cancel();/// <summary>/// 获取任务执行时间/// </summary>/// <returns></returns>public DateTime? GetNextTime();}

框架特点是啥

只有一个字,轻。用的舒服点。有问题大家一起沟通

框架地址

https://github.com/kesshei/TimeWheelDemo

引用链接

[1] .Net 之时间轮算法(终极版): #https://blog.csdn.net/i2blue/article/details/123608471

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/285211.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

phpstorm config include paths for swoole

配置phpstorm 当你写swoole 类或者函数时会自动补全 https://github.com/swoole/ide-helper.git 克隆下这个工具包 点加&#xff0c;然后指定你下载好的工具包路径&#xff0c;点ok 本文转自 skinglzw 51CTO博客&#xff0c;原文链接&#xff1a;http://blog.51cto.com/sking…

C语言试题150之八进制转换为十进制

📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 ✅作者简介:大家好,我是码莎拉蒂,CSDN博客专家(全站排名Top 50),阿里云博客专家、51CTO博客专家、华为云享专家 1、题目 题目:八进制转换为十进制 2 、温馨…

【ArcGIS风暴】ArcGIS创建栅格数据集色彩映射表案例--以GlobeLand30土地覆盖数据为例

矢量数据快速符号化&#xff0c;可以将常用的样式保存到样式符号库&#xff0c;栅格数据快速符号化&#xff0c;需要创建色彩映射表。本文以GlobeLand30土地覆盖数据为例&#xff0c;详解ArcGIS中创建与使用色彩映射表。 文章目录一、 ArcGIS色彩映射表介绍二、土地覆盖数据色彩…

Visual Studio 2019 16.3.10 初体验

Visual Studio 2019 版本 16.3.10 发布时间&#xff1a;2019 年 11 月 20 日 官网地址&#xff1a;https://visualstudio.microsoft.com/zh-hans/vs/ 介绍&#xff1a; https://devblogs.microsoft.com/visualstudio/dot-net-core-support-in-visual-studio-2019-version-16…

【ArcGIS风暴】GlobeLand30全球数据处理教程(批量投影转换、无效值处理、拼接)

本文讲述GlobeLand30全球数据批处理流程&#xff0c;主要步骤包括&#xff1a;批量分幅投影转换、批量分幅无效值处理、批量图幅拼接和成品展示。由于图幅数目和数据量较大&#xff0c;本文是采用Python代码批量实现的&#xff0c;有关Python的基本操作及处理案例&#xff0c;可…

Docker 私有仓库的搭建

Docker在2015年推出了distribution项目&#xff0c;即Docker Registry 2。相比于old registry&#xff0c;Registry 2使用Go实现&#xff0c;在安全性、性能方面均有大幅改进。Registry设计了全新的Rest API&#xff0c;并且在image存储格式等方面不再兼容于old Registry。去年…

请查收.NET MAUI 的最新学习资源

点击上方蓝字关注我们&#xff08;本文阅读时间&#xff1a;3分钟)2022 年 5 月 23 日&#xff0c;.NET MAUI 正式发布。.NET MAUI 为您提供了一流的跨平台 UI 堆栈&#xff0c;面向 Android、iOS、macOS 和 Windows。我们很高兴地宣布&#xff0c;有几种不同的学习 .NET MAUI …

C语言试题151之求 0到7 所能组成的奇数个数。

📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 ✅作者简介:大家好,我是码莎拉蒂,CSDN博客专家(全站排名Top 50),阿里云博客专家、51CTO博客专家、华为云享专家 1、题目 题目:求 0—7 所能组成的奇数个数…

腾讯2016春招之算法编程解析

第一道题&#xff1a;求有删除情况的最长回文子串 题目&#xff1a; 解题思路&#xff1a; 这个题严格意义上来说&#xff0c;删除了字符就谈不上回文串了&#xff0c;既然有删除&#xff0c;那估计考察的不是回文串&#xff0c;而是其他的&#xff0c;但是这个东西又有回文串的…

【EPS精品教程】EPS2016三维测图版安装教程(附EPS2016安装包下载地址)

文章目录 一、安装过程二、软件安装包下载EPS地理信息工作站是北京清华山维新技术开发有限公司历经十五年精心研发和打造,为满足“以地理信息服务为中心”的信息化测绘生产需求而推出的测绘生产活动多种业务模块集成化软件系统。主要功能有: (1)测绘与地理信息多业务模块集…

禁锢自己的因素,原来有这么多

2022年的7月&#xff0c;朋友圈都能看到喜庆的时刻&#xff0c;庆祝香港回归25周年&#xff0c;这确实是一个具有伟大里程碑的意义。同时也是建党101周年&#xff0c;满满的荣誉感&#xff0c;隔着朋友圈都能感受到喜庆。家事国事天下事&#xff0c;事事关心&#xff0c;关心但…

C语言试题152之一个偶数总能表示为两个素数之和

📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 ✅作者简介:大家好,我是码莎拉蒂,CSDN博客专家(全站排名Top 50),阿里云博客专家、51CTO博客专家、华为云享专家 1、题目 题目:一个偶数总能表示为两个素数…

[转]Xshell连接win10 Linux子系统

配置SSH服务&#xff1a; sudo apt-get remove --purge openssh-server ## 先删ssh sudo apt-get install openssh-server ## 在安装ssh sudo rm /etc/ssh/ssh_config ## 删配置文件&#xff0c;让ssh服务自己想办法链接 sudo service ssh --full…

记一个并发规则验证实现

最近在做一个简单的风控&#xff0c;其中有一块需求是这样的&#xff0c;当主请求参数到达后&#xff0c;会根据这些参数&#xff0c;看调起几个并发规则&#xff0c;这些规则各自有自己的验证逻辑&#xff0c;每个规则执行时间长短都不确定&#xff0c;当规则 执行完后&#x…

C语言试题153之判断一个素数能被几个 9 整除

📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 ✅作者简介:大家好,我是码莎拉蒂,CSDN博客专家(全站排名Top 50),阿里云博客专家、51CTO博客专家、华为云享专家 1、题目 题目:判断一个素数能被几个 9 整除…

[转]Zookeeper入门看这篇就够了

Zookeeper是什么 官方文档上这么解释zookeeper&#xff0c;它是一个分布式服务框架&#xff0c;是Apache Hadoop 的一个子项目&#xff0c;它主要是用来解决分布式应用中经常遇到的一些数据管理问题&#xff0c;如&#xff1a;统一命名服务、状态同步服务、集群管理、分布式应用…

微服务-springcloud(eureka实践, nacos实践)

Spring 体系图 版本关系 eureka 实践 1 父工程依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.14</version> </parent> <dependencyManage…

Windows服务二:测试新建的服务、调试Windows服务

一、测试Windows服务 为了使Windows服务程序能够正常运行&#xff0c;我们需要像创建一般应用程序那样为它创建一个程序的入口点。像其他应用程序一样&#xff0c;Windows服务也是在Program.cs的Main()函数中完成这个操作。首先我们在Main&#xff08;&#xff09;函数中创建一…

角度前方交会点坐标计算完整步骤

测量工作中&#xff0c;我们常常会遇到待测点被障碍物遮挡住观测视线而无法进行观测的情况。这时候我们就需要特殊的交会计算方法对待定点进行特别的观测。 前方交会又称为测角交会&#xff0c;是指从相邻两个已知点向待定点观测两个水平角&#xff0c;用以计算待定点的坐标。 …