Lazy Scheduler:我的轻量级任务调度框架

 

一、背景

工作中经常涉及任务调度,一直都是采用while(true) => if hitted DO => Thread.Sleep(interval)的模式。但是最近实在是感觉这种实现模式很挫。并且没有考虑到性能问题,需要撞击n次才能命中一次,使用效率不足5%(一百次while循环命中不到5次),但是单方面加大线程睡眠时间又无法保证高准确性和高精度。那有么有其它好的思路:即可以保持高准确性、高精度,又不浪费资源呢?

 二、我的思路

上述的短板在于:无目的的线程Sleep,如果我们可以每次恰到好处的Sleep,即线程被唤醒后刚好赶上下一次的任务到来,命中率就是100%了嘛。所以可以这样做:进入While后先按照Scheduler计算出下次的运行时间距离现在还有多久(Interval),然后直接Sleep(Interval),等待线程唤醒后就立即执行下一个既定Task就可以了。那这样做唯一的难点就在于你的Scheduler是否可计算、是否可以计算出下一次的运行时间点。

还好,我碰到的逻辑都还是满足这一点需求的:

(1)每隔一段时间执行一次的计划任务;

(2)在指定时间点必须执行的计划任务;

其他人性化设定:周六周日不运行。

三、代码实现

(1)主要有:负责调度的接口:IScheduler;Scheduler设定:ISchedulerSetting;任务接口:ITask以及一些其他的辅助类。

(2)难点:计算Interval的实现类:FixTimeSchedulerSetting.cs和IntervalSchedulerSetting.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//Author:      night-king
// Email:       deepleo@163.com
//Home:       http://deepleo.com
//Date:         2013-11-1
//Place:        Wuhan@China
 
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
 
namespace Deepleo.LazyScheduler.Setting
{
    public class FixTimeSchedulerSetting : IFixTimeSchedulerSetting, IWeekSchedulerSetting
    {
        public IList<DayOfWeek> ExcludeWeeks
        {
            get;
            set;
        }
 
        public int Hour
        {
            get;
            set;
        }
 
        public int Minutes
        {
            get;
            set;
        }
 
        public int Second
        {
            get;
            set;
        }
 
        public FixTimeSchedulerSetting(IList<DayOfWeek> excludeWeeks)
        {
            ExcludeWeeks = excludeWeeks;
        }
 
        public virtual TimeSpan CalculateNext()
        {
            var nowDateTime = System.DateTime.Now;
            var expectNextTime = System.DateTime.Parse(string.Format("{0}-{1}-{2} {3}:{4}:{5}", nowDateTime.Year, nowDateTime.Month, nowDateTime.Day, Hour, Minutes, Second));
            var todayWeek = nowDateTime.DayOfWeek;
            var km = new WeekManager(ExcludeWeeks);
            var delayDays = km.CalculateDelayDays(todayWeek);
            if (delayDays == 0)// this day of week can do
            {
                if (expectNextTime > nowDateTime) return expectNextTime - nowDateTime;
                else delayDays++;
            }
            return expectNextTime.AddDays(delayDays) - nowDateTime;
        }
    }
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System;
using System.Collections.Generic;
using System.Text;
 
namespace Deepleo.LazyScheduler.Setting
{
    public class IntervalSchedulerSetting : IIntervalSchedulerSetting, IWeekSchedulerSetting
    {
 
        public TimeSpan Interval
        {
            get;
            set;
        }
 
        public IList<DayOfWeek> ExcludeWeeks
        {
            get;
            set;
        }
 
        public IntervalSchedulerSetting(IList<DayOfWeek> excludeWeeks)
        {
            ExcludeWeeks = excludeWeeks;
        }
 
        public TimeSpan CalculateNext()
        {
            var nowDateTime = System.DateTime.Now;
            var todayWeek =nowDateTime.DayOfWeek;
            var km = new WeekManager(ExcludeWeeks);
            var delayDays = km.CalculateDelayDays(todayWeek);
            var interval = Interval.Add(new TimeSpan(delayDays, 0, 0, 0));
            if (interval.CompareTo(new TimeSpan(0, 0, 1)) <= 0) return new TimeSpan(0, 0, 1);
            else return interval;
        }
    }
}

重要的辅助类:WeekManager的实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//Author:      night-king
// Email:       deepleo@163.com
//Home:       http://deepleo.com
//Date:         2013-11-1
//Place:        Wuhan@China
 
using System;
using System.Collections.Generic;
using System.Text;
 
namespace Deepleo.LazyScheduler.Setting
{
  
    public class WeekManager
    {
        private Dictionary<int, DayOfWeek> _allowWeekDict;
 
        public WeekManager(IList<DayOfWeek> notAllowWeeks)
        {
            _allowWeekDict = new Dictionary<int, DayOfWeek>();
            if (!notAllowWeeks.Contains(DayOfWeek.Sunday))
                _allowWeekDict.Add(0, DayOfWeek.Sunday);
            if (!notAllowWeeks.Contains(DayOfWeek.Monday))
                _allowWeekDict.Add(1, DayOfWeek.Monday);
            if (!notAllowWeeks.Contains(DayOfWeek.Tuesday))
                _allowWeekDict.Add(2, DayOfWeek.Tuesday);
            if (!notAllowWeeks.Contains(DayOfWeek.Wednesday))
                _allowWeekDict.Add(3, DayOfWeek.Wednesday);
            if (!notAllowWeeks.Contains(DayOfWeek.Thursday))
                _allowWeekDict.Add(4, DayOfWeek.Thursday);
            if (!notAllowWeeks.Contains(DayOfWeek.Friday))
                _allowWeekDict.Add(5, DayOfWeek.Friday);
            if (!notAllowWeeks.Contains(DayOfWeek.Saturday))
                _allowWeekDict.Add(6, DayOfWeek.Saturday);
        }
 
        /// <summary>
        /// Calcute how many delay days
        /// </summary>
        /// <param name="week">current day of week</param>
        /// <returns>delay days</returns>
        public int CalculateDelayDays(DayOfWeek week)
        {
            var weekOfDay = (int)week;
            int distence = 0;
            while (true)
            {
                if (_allowWeekDict.ContainsKey(weekOfDay))
                {
                    return distence;
                }
                else
                {
                    weekOfDay = weekOfDay < 6 ? weekOfDay + 1 : 0;
                    distence++;
                }
            }
        }
    }
}

四、测试

(1)5s钟时间间隔的测试结果:

(2)1s测试结果:

再看CPU占用情况:

基本上稳定在0%,内存占用也只有6.4k.

五、代码下载:

http://files.cnblogs.com/deepleo/SchedulerSolution.zip

https://github.com/night-king/LazyScheduler

Lazy Scheduler的更多相关文章

  1. Lazy Scheduler:我的轻量级任务调度框架

    一.背景 工作中经常涉及任务调度,一直都是采用while(true) => if hitted DO => Thread.Sleep(interval)的模式.但是最近实在是感觉这种实现模 ...

  2. AndroidStudio3.0无法打开Android Device Monitor的解决办法(An error has occurred on Android Device Monitor)

    ---恢复内容开始--- 打开monitor时出现 An error has occurred. See the log file... ------------------------------- ...

  3. 代码的坏味道(15)——冗余类(Lazy Class)

    坏味道--冗余类(Lazy Class) 特征 理解和维护类总是费时费力的.如果一个类不值得你花费精力,它就应该被删除. 问题原因 也许一个类的初始设计是一个功能完全的类,然而随着代码的变迁,变得没什 ...

  4. Mach-O 的动态链接(Lazy Bind 机制)

    ➠更多技术干货请戳:听云博客 动态链接 要解决空间浪费和更新困难这两个问题最简单的方法就是把程序的模块相互分割开来,形成独立的文件,而不再将它们静态的链接在一起.简单地讲,就是不对那些组成程序的目标文 ...

  5. 从scheduler is shutted down看程序员的英文水平

    我有个windows服务程序,今天重点在测试系统逻辑.部署后,在看系统日志时,不经意看到一行:scheduler is shutted down. 2016-12-29 09:40:24.175 {& ...

  6. Spring 4 + Quartz 2.2.1 Scheduler Integration Example

    In this post we will see how to schedule Jobs using Quartz Scheduler with Spring. Spring provides co ...

  7. VMware中CPU分配不合理以及License限制引起的SQL Scheduler不能用于查询处理

    有一台SQL Server(SQL Server 2014 标准版)服务器中的scheduler_count与cpu_count不一致,如下截图所示: SELECT  cpu_count ,      ...

  8. Lazy Load, 延迟加载图片的 jQuery 插件.

    Lazy Load 是一个用 JavaScript 编写的 jQuery 插件. 它可以延迟加载长页面中的图片. 在浏览器可视区域外的图片不会被载入, 直到用户将页面滚动到它们所在的位置. 这与图片预 ...

  9. Hibernate之lazy延迟加载

    一.延迟加载的概念 当Hibernate从数据库中加载某个对象时,不加载关联的对象,而只是生成了代理对象,获取使用session中的load的方法(在没有改变lazy属性为false的情况下)获取到的 ...

随机推荐

  1. Effective C++ 7

    7.预先准备足够的内存情况. new当内存分配请求无法完成,它会抛出一个异常,怎么办异常,这是一个非常现实的,绝对必要的所遇到的问题后. 于c一般使用宏来分配内存和测试分发成功.c++中产阶级似下面的 ...

  2. 经典算法题每日演练——第八题 AC自动机

    原文:经典算法题每日演练--第八题 AC自动机 上一篇我们说了单模式匹配算法KMP,现在我们有需求了,我要检查一篇文章中是否有某些敏感词,这其实就是多模式匹配的问题. 当然你也可以用KMP算法求出,那 ...

  3. jQuery UI的datepicker日期控件如何让他显示中文

    首先是引入UI的JS文件和模板文件,如下: <link rel=”stylesheet” href=”./ui/themes/le-frog/jquery.ui.all.css”> < ...

  4. java_Eclipse中SVN的安装步骤(两种)和使用方法

    若是只要site地址: http://subclipse.tigris.org/update_1.6.x,  下边可以忽略 一.给Eclipse安装SVN,最常见的有两种方式:手动方式和使用安装向导方 ...

  5. 【Leetcode】Sort List (Sorting)

    这个问题需要与归并排序排两个名单,基本思路分为切割与合并 合并后的代码Merge Two Sorted List里已经讲得非常清楚了. 所以这里直接给出代码. public ListNode merg ...

  6. 1秒破解 js packer 加密

    原文:1秒破解 js packer 加密 其实有点标题党了,不过大概就是这个意思. 进入正题, eval(function(p,a,c,k,e,d){e=function(c){return(c< ...

  7. 采用malloc分别分配2KB个人空间,然后,realloc调整到6KB、1MB、3MB、10MB场地,分别这五内存“A”、“B”、“C”、“D”、“E”灌装

    #include<stdio.h> #include<stdlib.h> #include<string.h> #include<malloc.h> i ...

  8. C#函数式编程-高阶函数

    随笔分类 -函数式编程 C#函数式编程之标准高阶函数 2015-01-27 09:20 by y-z-f, 344 阅读, 收藏, 编辑 何为高阶函数 大家可能对这个名词并不熟悉,但是这个名词所表达的 ...

  9. 由一个LED闪烁问题发现的MTK的LED driver中存在的问题

    今天依据最新的需求要对LED灯的提示闪烁频率进行改动,将之前默认的2000ms改为10000ms,可是改动之后没有产生预料中的效果,而是变成了常量,百思不得其解,最后还是read the fuckin ...

  10. ArcGIS课程:表面数据转换成矢量数据

    虽然TIN (TIN) 和 terrain 数据收集被认为是载体表面.但它们实际上包括基于其他信息元素.并且该信息是在图象点.线或多边形原始格这可能是更实用的公式.在 ArcGIS 在,你可以很容易的 ...