一、引言
自然界中定时任务无处不在,太阳每晚东升西落red hat linux,每晚按量下班,每位月按量交租金,从某种意义上说,都可以觉得是定时任务。
众所周知,当须要处理一个高精度定时任务时仅借助cpu调度寻址时间片,其精确度是无法满足要求的。怎样设计一个高效的定时的任务,本文就来阐述这个问题。
二、定时器介绍
定时器在平常的业务开发中普遍存在于各个场景中,比如:
使用TCP长联接时,顾客端须要定时向服务端发送脉搏恳求。游戏中心每周一晚上10点推送近两天的crash报表。双11的0点,定时开启秒杀开关。。。。。。。
通常定时任务的方式表现为:经过固定时间后触发、按照固定频度周期性触发、在某个时刻触发。那定时器在计算机中底层是哪些?可以理解为这样一个数据结构:
判定一个任务是否到期,基本会采用协程的方法,每隔一个时间片去检测近来的任务是否到期,但是在添加任务和取消任务的行为发生以后,任务调度策略也会出现调整。
简而言之,定时器的实现还是靠线程协程实现的。
为了达到高效且高精度的定时器,要解决如下几个问题:
主协程时钟,不受超时任务的影响。设计任务管理器数据结构时须要该结构支持定时任务的高效添加、删除,其时间度复杂度为O(1)对于超时任务,非常是历时的超时任务须要单独处理,否则造成定时任务的精度。主协程时间细度适中。粗细度难以支持较小定时任务,细细度会给CPU导致额外的负担,浪费CPU资源。定时任务的周期性也须要考虑整个定时器占用较小的显存。三、数据结构设计
在选择数据结构时重点须要考虑2个数据结构的设计,一个是主协程时钟,一个任务集合的管理。关于主时钟的数据结构业内普遍的做法是:时间轮和最小堆。本文主要介绍时间轮的实现。
时间轮(TimingWheel)是GeorgeVarghese和TonyLauck在1996年的论文【HashedandHierarchicalTimingWheels:datastructurestoefficientlyimplementatimerfacility】实现的,它在Linux内核中使用广泛,是Linux内核定时器的实现方式和基础之一。时间轮是一种实现延后功能(定时器)的巧妙算法。假如一个系统存在大量的任务调度,时间轮可以高效的借助线程资源来进行批量化调度。把大批量的调度任务全部都绑定时间轮上,通过时间轮进行所有任务的管理,触发以及运行。就能高效地管理各类延时任务,周期任务,通知任务等。
任务管理的数据结构须要考虑:新增任务、删除任务、执行任务这三个指标linux操作系统介绍,一般来说时间复杂度越低越好。考虑到每位超时时间点所须要处理的超时任务存在多个,例如当前时间的后5秒,有3个超时任务。超时时间一到须要同时执行。结构形如:
暂时未能在飞书文档外展示此内容
里面的的任务管理方法,并未考虑新增任务、删除以及各任务之间优先级的关系。虽然采用HashTable、Set的方法也未能做到时间复杂度为O(1),此处我们选用单向数组的结构来管理任务集。
附单向数组的各项操作的时间复杂度:
插入操作删掉操作
删掉操作的时间复杂度和插入操作的时间复杂度类似。
四、时间轮原理介绍4.1时间轮结构介绍
时间轮(TimingWheel)算法应用范围十分广泛linux c++定时器,各类操作系统的定时任务调度都有用到,我们熟悉的LinuxCrontab,以及Java开发过程中常用的Dubbo、Netty、Akka、Quartz、ZooKeeper、Kafka等,几乎所有和时间任务调度都采用了时间轮的思想。
定时的任务调度分两种:
其实,这三者之间是可以互相转换的linux c++定时器,比如当前时间是12点,定时在5分钟以后执行,虽然绝对时间就是:12:05;定时在12:05执行,相对时间就是5分钟以后执行。
时间轮(TimingWheel)是一个储存定时任务的环型队列,底层采用链表实现,链表中的每位元素可以储存一个定时任务列表(TimerTaskList)。TimerTaskList是一个环型的单向数组,数组中的每一项表示的都是定时任务项(TimerTaskEntry),其中封装了真正的定时任务TimerTask。