crontab表达式和算法

来源:互联网 发布:单片机开发设计 编辑:程序博客网 时间:2024/05/28 03:02

crontab 表达式 语法

表达式

min hour day month week cmd

crontab表达式描述的是时间集合 */1 * * * * 表示所有的分钟集合(0-59)都会执行

crontab表达式各个栏位范围如下:

字段名是否必须允许的值允许的特定字符分(Minutes) 是0-59* / , -时(Hours)是0-23* / , -日(Day of month)/td>是1-31 * / , – ?月(Month)是1-12 or JAN-DEC* / , -星期(Day of week)否0-6 or SUM-SAT* / , – ?

先那分钟(min)这个栏位来说,我们用 int64 的二进制来标记分钟上的集合,从右往左第1位标识0分钟,第2位标记标识1分钟一直到第60位标记第59分钟.

用程序语言描述的话就是 第0分钟用 1<< 0 标识 第1分钟用 1<<1 标记 第59分钟就是 1<< 59。

“/”符号

ok,照上面说的方法 我们如何表示 */1 * * * * 分钟的所有集合呢?其实 “/”的右边表示集合范围,右边表示步长(step), 我们都可以用下面的算法算出他的标识方式

for i := min; i <= max; i += step {    bits |= 1 << i}

表示这个栏位最大的集合,上面的表达式

其实就是 1 << 0 | 1 <<1 | 1 <<2 ... 1<< 59 等同于 ^(MaxUint64 << 60) 等于

二进制 ( 111111111111111111111111111111111111111111111111111111111111)

那么同理 */2 * * * * 标识被2整除的集合就是 1<< 0 | 1<< 2 | 1<<4 .. 1<< 58 等于二进制

(010101010101010101010101010101010101010101010101010101010101)

补充说明下

crontab 表达式中 ‘/’ 后面的数值是步长(step) 可执行的时刻值可以理解为 范围内除以 步长 余数为 0 的时刻

例如*/7 * * * * crontab 在 并不是严格的每7分钟执行一次,他的二进制表示方法如下:

000100000010000001000000100000010000001000000100000010000001

从这个表达式中可以看出 /7 * * * 他的执行分钟时刻为0 7 14 21 28 35 42 49 56 下一个执行时间是 下一个小时的 0分钟 (并不是下个小时的1分钟)

“-“符号

“-”表示一个范围 2-9/2 * * * * 这种表达式 就是 2到9分钟 被2整除的所有集合 就是 2 4 6 8 表示为 1<<2 | 1 <<4 |1<< 6 | 1<< 8 同样它的标识方式时

for i := 2; i <= 9; i += 2 {    bits |= 1 << i}

“,”符号

“,”表示枚举值 比如表达式 1,5,9 * * * * 他表示的时1分钟,5分钟和9分钟 1<< 1 | 1<< 5 | 1<< 9

“?”符合

“?”只用于 日(Day of month) 和 星期(Day of week),表示不指定值,可以用于代替 *

其它栏位标识方式

分钟的标识方式已经清楚了,其它的四个栏位标识方式和它一样,唯一有差别的时 栏位的集合范围,比如 分钟是 0-59 小时是 0-23

完整表达式 “1-9/2 8-23 1,3,5 9 ?” 最后这个表达式的标识方式如下:

   min   : 1010101010   hour  : 111111111111111100000000   day   : 101010   month : 1000000000   week  : 1111111

时间排序和计算下次执行的时刻

step 1 – 遍历任务计算出每个任务下次执行的时间

首先看一下cron的结构体

   type Cron struct {        Schedule Schedule   //时间集合        Job job // cmd        Next time.Time  //下次执行时间        Prev time.Time  //上次执行时间    }

所以遍历的程序看起来像

   for i=0;i<len(cron_list);i++{        cron_list[i].Next = calculation(Schedule);//先跳过calculation 函数,后续会说明    }

step 2 – 对任务进行排序

获取到Next的是之后 我们需要对Cron进行按照Next 从小到大排序(最近要执行的排在最前面)

step 3 – 指定时间执行任务

从排过序的列表中拿到第一个cron 的 Next值,程序阻塞等Next时刻的时候执行(当然 需要拿到列表中相同Next的任务 并发执行 而且不能阻塞主进程)
主程序继续对任务列表计算Next值 重复 上面3个步骤。

ok,到了这边 主线流程逐渐清晰了。还剩下 calculation 这个函数的实现没说明,继续外下看。

calculation 函数实现

calculation 计算的是cron时间集合中最近要执行的时刻,那么如何具体如何计算的,

在cron 时间集合里头的 month 里头找到离当前时间最近的月份,然后离当前时间最近的天,然后离当前时间最近的小时 然后离当前时间最近的分钟 是不是就可以了

ok 按照这个思路 程序看起来是这个样子

   t = time.Now()   while( 1 << t.month() & Schedule.month == 0){      t.month = t.month +1  // 当前时间月份加1   }   #day 栏位比较特别  week实际上也是作用于天的,day 表示的是月份的天,所以对天数加的+1的时候 需要同时匹配day 和 week 所以程序   while (!(1<<t.Day() &s.Dom  && 1<<t.Weekday() &s.Dow )) {        t.Day = t.Day +1     }

下面hour min 类似,最后通过各种+ 操作的 t 就是这个job最近的时刻了。

0 0
原创粉丝点击