关于时间轮算法的起始

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





个人认为的问题

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

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

比如说当前的时间是 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. 关于transform属性的一些理解

    3D transform transform进行动画演示时,是以元素的中心为基准点的,可以使用transform-origin改变元素转变的基准点. 所有的transform动作改变都会引起X.Y轴的 ...

  2. 127_Power Pivot&Power BI DAX计算订单商品在库时间(延伸订单仓储费用)

    博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一.背景 前面已经写过一个先进先出的库龄案例,在业务发生又有这样一个需求:先进先出前提,需要按照订单计算每个商品在库时间, ...

  3. 个人冲刺(七)——体温上报app(二阶段)

    冲刺任务:完成特殊情况功能 MoreActivity.java package com.example.helloworld; import android.content.Intent; impor ...

  4. awk-文本处理【中文手册版】

    01. 简介 AWK是一个文本(面向行和列)处理工具,同时它也是一门脚本语言. AWK其名称得自于它的创始人 Alfred Aho .Peter Weinberger 和 Brian Kernigha ...

  5. 每天一个 HTTP 状态码 203

    203 Non-Authoritative Information 203 Non-Authoritative Information 'Non-Authoritative Informative' ...

  6. ARM学习1

    ARM相关概念 1.ARM的发展史 1. 1978年,CPU公司 Cambridge processing Unit 2. 1979年 Acorn 3. 1985年, 32位,8MHz, 使用的精简指 ...

  7. JVM学习笔记-从底层了解程序运行(二)

    解决JVM运行中的问题 一个案例理解常用工具 测试代码: /** * 从数据库中读取信用数据,套用模型,并把结果进行记录和传输 */ public class T15_FullGC_Problem01 ...

  8. 老子云AMRT全新三维格式正式上线,其性能全面超越现有的三维数据格式

    9月16日,老子云AMRT全新三维格式正式上线,其性能远超现有的三维数据格式.目前已有含国家超算长沙中心.中科院空间所.中车集团等上百家政企事业单位的项目中使用了AMRT格式,大大提升了可视化项目的开 ...

  9. 一次 MySQL 误操作导致的事故,「高可用」都顶不住了!

    这是悟空的第 152 篇原创文章 官网:www.passjava.cn 你好,我是悟空. 上次我们项目不是把 MySQL 高可用部署好了么,MySQL 双主模式 + Keepalived,来保证高可用 ...

  10. SAP Web Dynpro-版本管理

    您可以使用版本管理来管理对象的旧版本,比较版本,也可以重置它们. 在版本管理中,您可以存储ABAP开发对象的不同版本. 在ABAP工作台中,您可以比较不同版本的- 视图 视窗 控制器 您也可以存储对象 ...