在项目开发中经常会遇到后台定时任务调度执行计算的需求,为此我实现了一个简易的任务调度框架。

首先,我只实现的简易调度框架,原则上在同一时间点只执行一个任务,实现在每天指定的时间段内执行一次或固定频率(只是相对固定)执行多次任务。

其次,这个简易框架可用于windows 服务或asp.net网站实现后台定时调度任务计算。

要实现任务调度,使用核心技术的就是System.Timers.Timer对象。

下面代码实现:

1.定义内部使用的任务类Task;


简易任务调度实现--可用于windows服务/asp.net网站执行后台计算_任务调度简易任务调度实现--可用于windows服务/asp.net网站执行后台计算_任务调度_02View Code


1 private class Task
2 {
3 public TimeSpan Start; //每天任务开始的时间点,从00:00开始算起
4 public TimeSpan End; //每天任务结束的时间点,从00:00开始算起
5 public bool Once = false; //指定任务是否每天只执行一次
6 public DateTime Executed = DateTime.MinValue; //任务在最后一次执行的时间点
7 public TimeSpan Interval; //任务执行频率
8 public Action action; //匿名委托,用于任务计算
9 public override string ToString()
10 {
11 return string.Format(@"Task({0}) Start:{1},End:{2},Once:{3},Interval:{4},Action:{5},Executed:{6}", this.GetHashCode(), this.Start, this.End, this.Once, this.Interval, this.action, this.Executed);
12 }
13 }


2.任务调试类的主要接口(核心功能)


简易任务调度实现--可用于windows服务/asp.net网站执行后台计算_任务调度简易任务调度实现--可用于windows服务/asp.net网站执行后台计算_任务调度_02View Code


class Schedule : IDisposable
{
private class Task
{
public TimeSpan Start; //每天任务开始的时间点,从00:00开始算起
public TimeSpan End; //每天任务结束的时间点,从00:00开始算起
public bool Once = false; //指定任务是否每天只执行一次
public DateTime Executed = DateTime.MinValue; //任务在最后一次执行的时间点
public TimeSpan Interval; //任务执行频率
public Action action; //匿名委托,用于任务计算
public override string ToString()
{
return string.Format(@"Task({0}) Start:{1},End:{2},Once:{3},Interval:{4},Action:{5},Executed:{6}", this.GetHashCode(), this.Start, this.End, this.Once, this.Interval, this.action, this.Executed);
}
}

/// <summary>
/// 在每天指定的时间段内以相对固定的频率执行任务
/// </summary>
/// <param name="start">任务开始时间点,从00:00开始算起</param>
/// <param name="end">任务结束时间点,从00:00开始算起</param>
/// <param name="interval">任务调试效率</param>
/// <param name="action">匿名委托,用于任务计算</param>
public void Execute(TimeSpan start, TimeSpan end, TimeSpan interval, Action action)
{
AddTask(new Task()
{
action = action,
End = end,
Interval = interval,
Once = false,
Start = start
});
}

/// <summary>
/// 在每天指定的时间段内执行一次任务
/// </summary>
/// <param name="start">任务开始时间点,从00:00开始算起</param>
/// <param name="end">任务结束时间点,从00:00开始算起</param>
/// <param name="action">匿名委托,用于任务计算</param>
public void ExecuteOnce(TimeSpan start, TimeSpan end, Action action)
{
AddTask(new Task()
{
action = action,
End = end,
Once = true,
Start = start
});
}

/// <summary>
/// 开始调度任务
/// </summary>
public void Start()
{
this.onGoing = true;
this.timer.Enabled = true;
}

/// <summary>
/// 结束高度任务
/// </summary>
public void Stop()
{
this.onGoing = false;
this.timer.Enabled = false;
}


private System.Timers.Timer timer;//timer对象,用于定时检时任务执行点
private volatile bool onGoing = false; //指示任务调度是否持续运行
private List<Task> tasks = new List<Task>(); //任务列表,用于任务排序

/// <summary>
/// 构造函数,默认以5秒的频率检查任务执行时间点
/// </summary>
public Schedule()
: this(5000)
{

}

/// <summary>
/// 构造函数,默认以指定的频率检查任务执行时间点
/// </summary>
/// <param name="interval"></param>
public Schedule(double interval)
{
timer = new System.Timers.Timer(interval);
timer.AutoReset = false; //这是整个框架实现的关键所在,后面会进一步说明
timer.Enabled = false;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed); //用于检查任务时间点的匿名委托
}

/// <summary>
/// 添加一个任务到任务列表
/// </summary>
/// <param name="task"></param>
private void AddTask(Task task)
{
if (task.Once == true)
tasks.Insert(0, task); //每天一次的任务优先
else
tasks.Add(task);
}


/// <summary>
/// 用于检查任务时间点的匿名委托
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (this.onGoing == true) //判断调度是否继续
{
if (tasks.Count > 0)
{
int index = 0;
while (true)
{
Task task = tasks[index++]; //获取任务列表中的第一个任务

if (this.onGoing == false) break; //判断调度是否继续

if (CompareTime(task)) //比较任务时间点
{
ExecuteTask(task); //执行任务,同时更新任务最后执行时间点
SortTasks(); //对任务列表进行排序,确定下一次最先执行的任务
break;
}
}
}
}

if (this.onGoing == true)//判断调度是否继续
{
this.timer.Enabled = true; //激活timer,在指定时间再进一次任务时间点检查
}
}

/// <summary>
/// 任务列表排序,将一次性的任务排在前面,将最近已执行的任务放在最后
/// </summary>
private void SortTasks()
{
if (tasks.Count <= 1) return;
tasks.Sort(new Comparison<Task>(delegate(Task Task0, Task Task1)
{
if (Task0.Once == true && Task1.Once == false)
{
return -1;
}

if (Task0.Once = true && Task1.Once == true)
{
return Task0.Executed < Task1.Executed ? -1 : 1;
}

if (Task0.Once == false && Task1.Once == true)
{
return 1;
}

return Task0.Executed < Task1.Executed ? -1 : 1;
}));

}

/// <summary>
/// 比较任务时间点
/// </summary>
/// <param name="task">任务</param>
/// <returns></returns>
private bool CompareTime(Task task)
{

DateTime Now = DateTime.Now; //当前时间点

//计算任务在当天内开始执行时间
DateTime startTime = new DateTime(Now.Year, Now.Month, Now.Day, task.Start.Hours, task.Start.Minutes, task.Start.Seconds);
//计算任务在当天内结束执行时间
DateTime endTime = new DateTime(Now.Year, Now.Month, Now.Day, task.End.Hours, task.End.Minutes, task.End.Seconds);
//任务跨天执行,调整时间点
if (startTime > endTime) endTime += new TimeSpan(24, 0, 0);

//如果是每天一次的任务,而且已经执行过一次
if (task.Once == true && startTime <= task.Executed && task.Executed <= endTime)
{
return false;
}

return startTime <= Now && Now <= endTime; //如果当前时间点,在任务执行时间段内,则返真
}

/// <summary>
/// 执行任务
/// </summary>
/// <param name="task"></param>
private void ExecuteTask(Task task)
{
if (task.action != null)
{
//如果当前时间在任务执行频率内,则执行
if (task.Executed == DateTime.MinValue || DateTime.Now - task.Executed >= task.Interval)
{
task.action();
task.Executed = DateTime.Now; //更新任务最后执行时间
}
}
}

#region IDisposable Members

public void Dispose()
{
this.timer.Dispose();
}

#endregion
}


​复制代码​

3.在asp.net网站中调度任务


简易任务调度实现--可用于windows服务/asp.net网站执行后台计算_任务调度简易任务调度实现--可用于windows服务/asp.net网站执行后台计算_任务调度_02View Code


public class Global : System.Web.HttpApplication
{

Schedule schedule = new Schedule(10000);
/// <summary>
/// IIS启动时
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Application_Start(object sender, EventArgs e)
{
//每天从11:30至17:30,每隔一分钟执行一次任务
schedule.Execute(new TimeSpan(11, 30, 0), new TimeSpan(17, 30, 0), new TimeSpan(0,1,0), delegate()
{
AutoQuery.Execute();
});

//每天从11:00至11:20,只执行一次任务
schedule.ExecuteOnce(new TimeSpan(11, 0, 0), new TimeSpan(11, 20, 0), delegate()
{
AutoQuery.PrepareTasks();
});

//开始任务调度
schedule.Start();
}

protected void Application_End(object sender, EventArgs e)
{
//停止调度任务
schedule.Stop();
}

protected void Session_Start(object sender, EventArgs e)
{

}

protected void Application_BeginRequest(object sender, EventArgs e)
{

}

protected void Application_AuthenticateRequest(object sender, EventArgs e)
{

}

protected void Application_Error(object sender, EventArgs e)
{

}

protected void Session_End(object sender, EventArgs e)
{

}


}


复制代码

这此代码已经在项目中使用,已经通过本人多次测试。

完.