一、背景
工作中经常涉及任务调度,一直都是采用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
class="brush:csharp;gutter:true;">//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