zoukankan      html  css  js  c++  java
  • 简易任务调度实现可用于windows服务/asp.net网站执行后台计算

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

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

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

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

    下面代码实现:

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

    View 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.任务调试类的主要接口(核心功能)

    View 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网站中调度任务

    View 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)
    {

    }


    }

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

    完.




  • 相关阅读:
    Synchronized 锁 批量重偏向 和批量撤销
    Synchronize 偏向锁,轻量级锁升级和撤销过程,对象头的变化
    JAVA 对象到底有什么
    什么是操作系统虚拟地址
    从C角度看 i = i+ 1本质
    Linux操作系统的三种锁机制
    SpringCloud启动过程中如何完成Nacos服务注册
    Nacos源码一
    JAVA线程的本质
    SpringCloud启动-Nacos服务注册
  • 原文地址:https://www.cnblogs.com/flysun/p/2317570.html
Copyright © 2011-2022 走看看