关于时间轮算法的起始

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





个人认为的问题

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

假设我们现在有一个很大的数组,专门用于存放延时任务。它的精度达到了毫秒级!那么我们的延迟任务实际上需要将定时的那个时间简单转换为毫秒即可,然后将定时任务存入其中:

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

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

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

为什么要用时间轮实现

  1. 通常用于实现 linux 内核任务、游戏类的 buf 计时。
  2. 单个时间轮局限性:保存的任务数量少,不能超过当前时间轮。
  3. 多层时间轮,典型:日-时-分-秒
  4. 传统 java 实现定时:Timer,只能单线程,会阻塞; Executors.newScheduledThreadPoll, 使用的最小堆来实现,任务还是不能太多,添加时间复杂度为 O(logn)

时间轮定时器最大的优点

  1. 是任务的添加与移除,都是 O(1)级的复杂度;
  2. 不会占用大量的资源;
  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);
}
}

最后的效果

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

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

github 地址

https://github.com/kesshei/TimeWheelDemo

.Net 之时间轮算法(终极版)的更多相关文章

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

    TimeWheelDemo 一个基于时间轮原理的定时器 对时间轮的理解 其实我是有一篇文章(.Net 之时间轮算法(终极版))针对时间轮的理论理解的,但是,我想,为啥我看完时间轮原理后,会采用这样的方 ...

  2. 时间轮算法的定时器(Delphi)

    源码下载 http://files.cnblogs.com/lwm8246/uTimeWheel.rar D7,XE2 编译测试OK //时间轮算法的定时器 //-- : QQ unit uTimeW ...

  3. 时间轮算法(TimingWheel)是如何实现的?

    前言 我在2. SOFAJRaft源码分析-JRaft的定时任务调度器是怎么做的?这篇文章里已经讲解过时间轮算法在JRaft中是怎么应用的,但是我感觉我并没有讲解清楚这个东西,导致看了这篇文章依然和没 ...

  4. 时间轮算法在Netty和Kafka中的应用,为什么不用Timer、延时线程池?

    大家好,我是yes. 最近看 Kafka 看到了时间轮算法,记得以前看 Netty 也看到过这玩意,没太过关注.今天就来看看时间轮到底是什么东西. 为什么要用时间轮算法来实现延迟操作? 延时操作 Ja ...

  5. 延时任务-基于netty时间轮算法实现

    一.时间轮算法简介 为了大家能够理解下文中的代码,我们先来简单了解一下netty时间轮算法的核心原理 时间轮算法名副其实,时间轮就是一个环形的数据结构,类似于表盘,将时间轮分成多个bucket(比如: ...

  6. [从源码学设计]蚂蚁金服SOFARegistry之时间轮的使用

    [从源码学设计]蚂蚁金服SOFARegistry之时间轮的使用 目录 [从源码学设计]蚂蚁金服SOFARegistry之时间轮的使用 0x00 摘要 0x01 业务领域 1.1 应用场景 0x02 定 ...

  7. kafka时间轮的原理(一)

    概述 早就想写关于kafka时间轮的随笔了,奈何时间不够,技术感觉理解不到位,现在把我之前学习到的进行整理一下,以便于以后并不会忘却.kafka时间轮是一个时间延时调度的工具,学习它可以掌握更加灵活先 ...

  8. 经典多级时间轮定时器(C语言版)

    经典多级时间轮定时器(C语言版) 文章目录 经典多级时间轮定时器(C语言版) 1. 序言 2. 多级时间轮实现框架 2.1 多级时间轮对象 2.2 时间轮对象 2.3 定时任务对象 2.4 双向链表 ...

  9. 08重编终极版《东邪西毒:终极版》DVD粤语中字

    1.东邪西毒].Ashes.of.Time.1994.384p.DVDRip.x264.ac3-DTMM.mkv 这个版本最清晰 ,可惜删减了,只有87分钟,粤语,1.4G. 2.东邪西毒(初始版). ...

随机推荐

  1. python二分法、牛顿法求根

    二分法求根 思路:对于一个连续函数,左值f(a)*右值f(b)如果<0,那么在这个区间内[a,b]必存在一个c使得f(c)=0 那么思路便是取中间点,分成两段区间,然后对这两段区间分别再比较,跳 ...

  2. JS 的 new 是个啥?

    JS 的 new 是个啥? 本文写于 2019 年 11 月 25 日 new关键字在很多语言里面,总是用于把类实例化,可是 JS 之前就没有"类"这个概念呀. 那 JS 的new ...

  3. Git 上传文件项目到github,gitee详细教程!(本文用的gitee)

    1:安装Git 下载地址:https://git-scm.com/ 2:生成ssh密钥不会的可以去另一篇文章 https://www.cnblogs.com/psfjc/p/15980893.html ...

  4. 面试突击49:说一下 JUC 中的 Exchange 交换器?

    Exchange(交换器)顾名思义,它是用来实现两个线程间的数据交换的,它诞生于 JDK 1.5,它有两个核心方法: exchange(V x):等待另一个线程到达此交换点,然后将对象传输给另一个线程 ...

  5. 超级简单!CentOS-8 安装 MySQL 8.0,比喝水还简单

    中国人不骗中国人 果然是系统和MySQL的版本越高安装越便利了 在阿里云的 CentOS-8 比喝开水还简单的安装 MySQL 8.0,开始~ 1.以 root 用户通过 CentOS 软件包管理器来 ...

  6. 2.0 vue2+tinymce实现图片上传与回显

    1.效果 2.配置 2.1 在init中添加图片上传函数 // 图片上传 images_upload_handler: (blobInfo, success, failure) => { // ...

  7. 75. Sort Colors - LeetCode

    Question 75. Sort Colors Solution 题目大意: 给一个数组排序,这个数组只有0,1,2三个元素,要求只遍历一遍 思路: 记两个索引,lowIdx初始值为0,highId ...

  8. OAuth2.0之OLTU实现举例

    一.场景 三个角色:用户(user),web应用(client),资源服务器和授权服务器合为服务器(server) 用户登录登录后可查看自己的信息 二.准备 2.1 数据库 schema drop t ...

  9. c++ web框架实现之静态反射实现

    0 前言 最近在写web框架,框架写好后,需要根据网络发来的请求,选择用户定义的servlet来处理请求.一个问题就是,我们框架写好后,是不知道用户定义了哪些处理请求的类的,怎么办? 在java里有一 ...

  10. 关于『HTML』:第一弹

    关于『HTML』:第一弹 建议缩放90%食用 根据C2024XSC212童鞋的提问, 我准备写一稿关于『HTML』基础的帖 But! 当我看到了C2024XSC130的 "关于『HTML5』 ...