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

关于时间轮算法的起始

我也认真的看了时间轮算法相关,大致都是如下的一个图

35bd4a941c2130e6282c495ffebbd327.png
在这里插入图片描述
9ea640890ae0531e6bd18bb17741e9fd.png
在这里插入图片描述
20273a2a892be278248b46095ee357cd.png
在这里插入图片描述
40a1360c00e3c57bd53ec22bf1e92810.png
在这里插入图片描述

个人认为的问题

大部分文章在解释这个为何用时间轮的时候都再说

假设我们现在有一个很大的数组,专门用于存放延时任务。它的精度达到了毫秒级!那么我们的延迟任务实际上需要将定时的那个时间简单转换为毫秒即可,然后将定时任务存入其中:比如说当前的时间是2018/10/24 19:43:45,那么就将任务存入Task[1540381425000],value则是定时任务的内容。假如这个数组长度达到了亿亿级,我们确实可以这么干。那如果将精度缩减到秒级呢?我们也需要一个百亿级长度的数组。先不说内存够不够,显然你的定时器要这么大的内存显然很浪费。

为什么要用时间轮实现

  1. 1. 通常用于实现linux内核任务、游戏类的buf计时。

  2. 2. 单个时间轮局限性:保存的任务数量少,不能超过当前时间轮。

  3. 3. 多层时间轮,典型:日-时-分-秒

  4. 4. 传统java实现定时:Timer,只能单线程,会阻塞;Executors.newScheduledThreadPoll, 使用的最小堆来实现,任务还是不能太多,添加时间复杂度为O(logn)

时间轮定时器最大的优点

  1. 1. 是任务的添加与移除,都是O(1)级的复杂度;

  2. 2. 不会占用大量的资源;

  3. 3. 只需要有一个线程去推进时间轮就可以工作了

举例说明
比如说当前的时间是2018/10/24 19:43:45,那么就将任务存入Task[1540381425000],value则是定时任务的内容。

private Task[很长] tasks;
public List<Task> getTaskList(long timestamp) {return task.get(timestamp)
}
// 假装这里真的能一毫秒一个循环
public void run(){while (true){getTaskList(System.currentTimeMillis()).后台执行()Thread.sleep(1);}
}

假如这个数组长度达到了亿亿级,我们确实可以这么干。那如果将精度缩减到秒级呢?我们也需要一个百亿级长度的数组。

先不说内存够不够,显然你的定时器要这么大的内存显然很浪费。


基于个人的理解对其进行改造和实现

对于以上的描述,我自己还是很不认同的,我为啥要声明这么大的数组,难道不能有一个任务,我就放一个任务么,实际数组的长度应该是你任务数的长度吧。

要不然,为啥要这么浪费。想不通,可能还有别的解释,谁有答案可以告诉我。

在实际的使用中,一般都为秒级,毫秒级都很少,因为毫秒级的不准。

所以,我可以根据这些通过hash字典构建一个 这样的时间轮算法,也作为我自己想实现定时任务框架的一部分。

逻辑:核心为一个字典,key 为时间戳,值为任务列表。整体就是每个添加的任务都有一个触发的时间(秒),到了这个秒,时间就会触发,自然会去执行相关的任务。有了任务才会添加,不会任何任务都添加的。任务触发的时候,会获取任务下一次执行的时间,并插入到任务列表里。

static void Main(string[] args){ScheduledTask scheduledTask = new ScheduledTask(() => { Console.WriteLine($"{DateTime.Now}"); }, new TimeSpan(0, 0, 5));TimeWheel timeWheel = new TimeWheel();timeWheel.AddTask(scheduledTask);timeWheel.Run();Console.WriteLine("开始运行时间轮!");Console.ReadLine();}
/// <summary>/// 时间轮算法(终极)实现/// 大部分都是支持秒级,所以,按照秒级进行实现/// </summary>public class TimeWheel{/// <summary>/// 任务列表/// </summary>public ConcurrentDictionary<long, List<IScheduledTask>> Tasks = new();public void Run(){while (true){var now = DateTime.Now;Task.Run(() => { Trigger(now); });var offset = 500 - now.Millisecond;SpinWait.SpinUntil(() => false, 1000 + offset);}}public void Trigger(DateTime dateTime){var timeStamp = GenerateTimestamp(dateTime);var oldTimeStamp = timeStamp - 1;Tasks.TryRemove(oldTimeStamp, out var _);Tasks.TryGetValue(timeStamp, out var result);if (result?.Any() == true){foreach (var item in result){Task.Run(item.GetAction());var NewTime = item.GetNextTime();if (NewTime.HasValue){AddTask(NewTime.Value, item);}}}}public void AddTask(IScheduledTask scheduledTask){var timeStamp = GenerateTimestamp(scheduledTask.GetNextTime().Value);Tasks.AddOrUpdate(timeStamp, new List<IScheduledTask>() { scheduledTask }, (k, v) =>{v.Add(scheduledTask);return v;});}public void AddTask(DateTime dateTime, IScheduledTask scheduledTask){var timeStamp = GenerateTimestamp(dateTime);Tasks.AddOrUpdate(timeStamp, new List<IScheduledTask>() { scheduledTask }, (k, v) =>{v.Add(scheduledTask);return v;});}private long GenerateTimestamp(DateTime dateTime){return new DateTimeOffset(dateTime.ToUniversalTime()).ToUnixTimeSeconds();}private DateTime GetDatetime(long timeStamp){var d = DateTimeOffset.FromUnixTimeSeconds(timeStamp);return d.LocalDateTime;}}
public interface IScheduledTask{public Action GetAction();public DateTime? GetNextTime();}/// <summary>/// 定时器任务,普通任务/// 间隔指定的时间/// </summary>public class ScheduledTask : IScheduledTask{private Action _action;private TimeSpan timeSpan;public ScheduledTask(Action action, TimeSpan timeSpan){this._action = action;this.timeSpan = timeSpan;}public Action GetAction(){return this._action;}public DateTime? GetNextTime(){return DateTime.Now.AddSeconds(timeSpan.TotalSeconds);}}

最后的效果

082195d5fa4792c821ac6f49c9b5a1e7.png
在这里插入图片描述

当然,也可以通过CRON表达式来实现更为高级的。

后期再来个更高级一点的。

github地址

https://github.com/kesshei/TimeWheelDemo

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

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

相关文章

C语言试题143之写一个函数,求一个字符串的长度,在 main 函数中输入字符串,并输出其长度

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

无人机影像去雾批处理神器使用方法(附神器下载)

文章目录 1. 软件特点:2. 影像去雾批处理演示3. 神器下载地址1. 软件特点: (1)设置简单。只需要设置输入输出路径。 (2)支持多级路径。路径下可以有子路径,输出文件夹结构与输入文件夹结构一致 (3)支持并行处理。可同时打开几个窗口,只需要设置同样路径即可 2. 影…

[转]nginx学习,看这一篇就够了:下载、安装。使用:正向代理、反向代理、负载均衡。常用命令和配置文件

文章目录 前言一、nginx简介 1. 什么是 nginx 和可以做什么事情2.Nginx 作为 web 服务器3. 正向代理4. 反向代理5. 负载均衡6.动静分离二、Nginx 的安装(Linux:centos为例) 1. 准备工作2. 开始安装3. 运行nginx4. 防火墙问题三、 Nginx 的常用命令和配置文件 1. Nginx常用命令 …

在 .NET 6 中使用 Startup.cs 更简洁的方法

如果您在关注 .NET 6&#xff0c;那么您应该知道&#xff0c;在 .NET 6 项目中&#xff0c;没有 Startup.cs 文件&#xff0c;现在使用了 Program.cs 文件来完成统一的配置。我之前发了一篇使用在 .NET 6 项目中使用 Startup.cs 的文章。在 .NET 6 项目中使用 Startup.cs能否能…

【ArcGIS Pro微课1000例】0005:ArcGIS Pro 2.5基于矢量数据制作拉伸三维地图案例

ArcGIS Pro 2.5中,可以基于某个字段,对矢量数据进行拉伸,制作精美的三维地图。本文以中国省级行政区划数据为例,基于面积字段制作3d地图。 文章目录 1. 新建局部场景2. 地图符号化3. 三维矢量地图制作1. 新建局部场景 打开ArcGIS Pro 2.5,新建局部场景项目,并保存。 2. …

C语言试题144之编写函数输入,输出 5 个学生的数据记录。

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

分布式(一致性协议)之领导人选举( DotNext.Net.Cluster 实现Raft 选举 )

分布式(一致性协议)之领导人选举( DotNext.Net.Cluster 实现Raft 选举 )继分布式锁之后的又一高可用技术爽文之分布式领导选举 或者说 分布式一致性协议的实现分布式选举是实现高可用的必备技术&#xff0c;想实现主从&#xff0c;就必须得有选举的策略&#xff0c;有主从才会有…

C语言试题146之反向输出一个链表

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

【ArcGIS Pro微课1000例】0006:ArcGIS Pro 2.5三维显示DEM数字高程模型

通过ArcGIS的学习,我们知道,ArcScene可以实现二维数据的三维显示,是将二维数据(例如DEM)进行自定义表面浮动拉伸。那么ArcGIS Pro中能不能实现DEM的三维显示呢? ArcScene三维显示结果: 目前所采用的ArcGIS Pro 2.5版本还不能直接将DEM进行三维显示,我们的做法是参照Ar…

程序员的自我修养:有助于提高沟通能力的7本书

直接影响工作效率的四种能力&#xff1a;沟通能力、自学能力、自我管理能力、问题解决能力。提高沟通能力&#xff0c;是程序员提高自我修养的必要条件。相信很多人跟我一样&#xff0c;性格内向&#xff0c;信仰技术&#xff0c;很少有跟人说话的愿望&#xff0c;只是想看代码…

玩转 Linux 之:磁盘分区、挂载知多少?

转载于&#xff1a;http://my.oschina.net/leejun2005/blog/290073 在做日志机扩容的时候&#xff0c;发现运维同学将一块硬盘的挂载点没有同以前的日志机保持一致&#xff0c;考虑到这会给日后的维护带来麻烦&#xff0c;于是尝试着手修改&#xff0c;在修改的同时&#xff0c…

C# NanoFramework 点灯和按键 之 ESP32

本来周末是要搞个大的&#xff0c;WIFI 和 Web网页之类的&#xff0c;奈何搞了两天&#xff0c;并与外国友人聊过后&#xff0c;才发现是固件有问题&#xff0c;晚上与大佬进行交流后才发现&#xff0c;原来ESP32S的官方固件有问题&#xff0c;搞不了。所以&#xff0c;建议买的…

【ArcGIS Pro微课1000例】0002:ArcGIS Pro 2.5二三维联动显示

ArcGIS Pro是一款全新的桌面应用程序,它改变了桌面GIS的工作方式,以满足新一代WebGIS应用模式。ArcGIS Pro采用Ribbon界面风格,给人全新的用户体验。它作为一个高级的应用程序,可以对来自本地、ArcGIS Online、或者Portal for ArcGIS的数据进行可视化、编辑、分析。同时,实…

C语言试题147之创建一个链表并且排序输出这个链表

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

PHP中刷新输出缓冲

2019独角兽企业重金招聘Python工程师标准>>> http://www.cnblogs.com/mutuan/archive/2012/03/18/2404957.html 转载于:https://my.oschina.net/wuzhencan/blog/652259

C语言试题145之创建一个链表

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

[转]IntelliJ IDEA 2019.3正式发布,给我们带来哪些新特性?

每篇一句 工欲善其事必先利其器 ——《论语卫灵公》 前言 千呼万唤始出来。自从JetBrains在今年7月24日发布了IDEA 2019.2版本后&#xff0c;从9月份开始我便一直在关注此版本正式版的发布。JetBrains公司在9月中旬就对外公布了下一个主要版本 2019.3的Roadmap&#xff0c;而且…

FineReport中以jws方式调用WebService数据源方案

在使用WebService作为项目的数据源时&#xff0c;希望报表中也是直接调用这个WebService数据源&#xff0c;而不是定义数据连接调用对应的数据库表&#xff0c;这样要怎么实现呢&#xff1f; 在程序中访问WebService应用服务&#xff0c;将WebService返回的数据转为程序数据集&…

C语言试题148之海滩上有一堆桃子,五只猴子来分。第一只猴子把这堆桃子凭据分为五份,多了一个,这只 猴子把多的一个扔入海中,拿走了一份。第二只猴子把剩下的桃子又平均分成五份,又多了 一个,它同样把多的

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

一文读懂什么是CTO、技术VP、技术总监、首席架构师

究竟什么是CTO&#xff0c;一个公司真的需要CTO么&#xff1f;哪些公司的职位对于技术管理者来讲真的是CTO的职位&#xff1f;同样是技术最高负责人&#xff0c;为什么有人叫CTO、有人叫技术总监、技术VP&#xff0c;有人叫首席架构师&#xff1f;他们之间的差别是什么&#xf…