简易任务调度实现--可用于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)
        {

        }

       
    }

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

完.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值