.Net 之时间轮算法(终极版)
关于时间轮算法的起始
我也认真的看了时间轮算法相关,大致都是如下的一个图




个人认为的问题
大部分文章在解释这个为何用时间轮的时候都再说
假设我们现在有一个很大的数组,专门用于存放延时任务。它的精度达到了毫秒级!那么我们的延迟任务实际上需要将定时的那个时间简单转换为毫秒即可,然后将定时任务存入其中:
比如说当前的时间是 2018/10/24 19:43:45,那么就将任务存入 Task[1540381425000],value 则是定时任务的内容。
假如这个数组长度达到了亿亿级,我们确实可以这么干。 那如果将精度缩减到秒级呢?我们也需要一个百亿级长度的数组。
先不说内存够不够,显然你的定时器要这么大的内存显然很浪费。
为什么要用时间轮实现
- 通常用于实现 linux 内核任务、游戏类的 buf 计时。
- 单个时间轮局限性:保存的任务数量少,不能超过当前时间轮。
- 多层时间轮,典型:日-时-分-秒
- 传统 java 实现定时:Timer,只能单线程,会阻塞; Executors.newScheduledThreadPoll, 使用的最小堆来实现,任务还是不能太多,添加时间复杂度为 O(logn)
时间轮定时器最大的优点
- 是任务的添加与移除,都是 O(1)级的复杂度;
- 不会占用大量的资源;
- 只需要有一个线程去推进时间轮就可以工作了
举例说明
比如说当前的时间是 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);
}
}
最后的效果

当然,也可以通过 CRON 表达式来实现更为高级的。
后期再来个更高级一点的。
github 地址
https://github.com/kesshei/TimeWheelDemo
.Net 之时间轮算法(终极版)的更多相关文章
- .Net之时间轮算法(终极版)定时任务
TimeWheelDemo 一个基于时间轮原理的定时器 对时间轮的理解 其实我是有一篇文章(.Net 之时间轮算法(终极版))针对时间轮的理论理解的,但是,我想,为啥我看完时间轮原理后,会采用这样的方 ...
- 时间轮算法的定时器(Delphi)
源码下载 http://files.cnblogs.com/lwm8246/uTimeWheel.rar D7,XE2 编译测试OK //时间轮算法的定时器 //-- : QQ unit uTimeW ...
- 时间轮算法(TimingWheel)是如何实现的?
前言 我在2. SOFAJRaft源码分析-JRaft的定时任务调度器是怎么做的?这篇文章里已经讲解过时间轮算法在JRaft中是怎么应用的,但是我感觉我并没有讲解清楚这个东西,导致看了这篇文章依然和没 ...
- 时间轮算法在Netty和Kafka中的应用,为什么不用Timer、延时线程池?
大家好,我是yes. 最近看 Kafka 看到了时间轮算法,记得以前看 Netty 也看到过这玩意,没太过关注.今天就来看看时间轮到底是什么东西. 为什么要用时间轮算法来实现延迟操作? 延时操作 Ja ...
- 延时任务-基于netty时间轮算法实现
一.时间轮算法简介 为了大家能够理解下文中的代码,我们先来简单了解一下netty时间轮算法的核心原理 时间轮算法名副其实,时间轮就是一个环形的数据结构,类似于表盘,将时间轮分成多个bucket(比如: ...
- [从源码学设计]蚂蚁金服SOFARegistry之时间轮的使用
[从源码学设计]蚂蚁金服SOFARegistry之时间轮的使用 目录 [从源码学设计]蚂蚁金服SOFARegistry之时间轮的使用 0x00 摘要 0x01 业务领域 1.1 应用场景 0x02 定 ...
- kafka时间轮的原理(一)
概述 早就想写关于kafka时间轮的随笔了,奈何时间不够,技术感觉理解不到位,现在把我之前学习到的进行整理一下,以便于以后并不会忘却.kafka时间轮是一个时间延时调度的工具,学习它可以掌握更加灵活先 ...
- 经典多级时间轮定时器(C语言版)
经典多级时间轮定时器(C语言版) 文章目录 经典多级时间轮定时器(C语言版) 1. 序言 2. 多级时间轮实现框架 2.1 多级时间轮对象 2.2 时间轮对象 2.3 定时任务对象 2.4 双向链表 ...
- 08重编终极版《东邪西毒:终极版》DVD粤语中字
1.东邪西毒].Ashes.of.Time.1994.384p.DVDRip.x264.ac3-DTMM.mkv 这个版本最清晰 ,可惜删减了,只有87分钟,粤语,1.4G. 2.东邪西毒(初始版). ...
随机推荐
- CSS展开收起
有一个问题是,在上述例子中,把段落内容的"浮动元素"去掉后,段落最后从"行"字开始换行了,"收起"却不换行,也就是会存在有两个字内容看不见情 ...
- re模块,正则表达式起别名和分组机制,collections模块,time与datetime模块,random模块
re模块和正则表达式别名和分组机制 命名分组 (1)分组--可以让我们从文本内容中提取指定模式的部分内容,用()来表示要提取的分组,需要注意的是分组 是在整个文本符合指定的正则表达式前提下进行的进一步 ...
- 用了Scrum越来越累?这三点帮你走出困境
摘要:你有没有一种感觉,团队用了Scrum之后,工作任务越来越多,加班越来越严重?有?好兄弟,这篇文章正好能帮你~ 本文分享自华为云社区<用了Scrum越来越累?这三点帮你走出困境>,作者 ...
- ptorch常用代码梯度篇(梯度裁剪、梯度累积、冻结预训练层等)
梯度裁剪(Gradient Clipping) 在训练比较深或者循环神经网络模型的过程中,我们有可能发生梯度爆炸的情况,这样会导致我们模型训练无法收敛. 我们可以采取一个简单的策略来避免梯度的爆炸,那 ...
- 20212115朱时鸿 《python程序设计》实验四报告
课程:<Python程序设计>班级: 2121姓名: 朱时鸿学号:20212115实验教师:王志强实验日期:2022年5月28日必修/选修: 公选课 1.实验内容 Python综合应用:爬 ...
- Unicode和中午互转
import java.io.UnsupportedEncodingException; public class TestUnicode{ /* * 中文转unicode编码 */ public s ...
- React项目配置npm run build命令分环境打包
使用create-react-app脚手架创建的项目默认隐藏了webpack等配置文件信息,使用npm run eject命令暴露这些隐藏的配置文件信息 项目默认有两个环境:开发环境(npm star ...
- tensorflow版本的bert模型 GPU的占用率为100%而其利用率为0%
Notice: 本方法只是解决问题的一种可能,不一定百分百适用,出现这个问题还有很多其他原因,这个可以作为解决的一种尝试!!! 经过检查发现,是由于激活环境的原因 使用 conda activate ...
- dotnet 使用 Crossgen2 对 DLL 进行 ReadyToRun 提升启动性能
我对几个应用进行严格的启动性能评估,对比了在 .NET Framework 和 dotnet 6 下的应用启动性能,非常符合预期的可以看到,在用户的设备上,经过了 NGen 之后的 .NET Fram ...
- Error: $controller:ctrlreg A controller with this name is not registered.
The controller with the name 'SomeController' is not registered.名称为'SomeController'的控制器没注册 : Descrip ...