• Lazy Scheduler


    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

  • 相关阅读:
    java.security.InvalidKeyException: Illegal key size aes解密失败
    Java获取URL链接的文件类型
    解决eclipse中web项目出现Project facet Java version 1.8 is not supported.的问题
    探讨一下Java单例设计模式
    HTML5文件上传qq、百度、taobao等比较(改进支持三种状态提示)
    jQuery Ajax 上传文件改进
    Java POI 3.17写入、导入EXCEL性能测试
    jQuery Ajax 上传文件夹及文件
    Java 打包下载服务器上选中的文件或目录(带进度条提示)
    pio 背景色
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3404421.html
Copyright © 2020-2023  润新知