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

//Author:      night-king
// Email:       deepleo@163.com
//Home:       http://deepleo.com
//Github:     https://github.com/night-king/
//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;
        }
    }
}

  

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的實現代碼:

//Author:      night-king
// Email:       deepleo@163.com
//Home:       http://deepleo.com
//Github:     https://github.com/night-king/
//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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM