在mvc项目中写一个线程的定时器(计划任务)
因为看见nop中有计划任务Task,是用于定时发送邮件、清理缓存、获取汇率、清理日志、删除访客记录,最近不是很忙,就把这块剥离了出来,以后如果用得到就可以直接拿来用。废话说到这里。下面上代码:
一个计划任务,主要包括这几个文件。
IScheduleTaskService是任务表的一些操作。
ITask是任务接口,定义类实行方法,是整个模块的核心接口
ITaskService是执行的任务接口,实现具体要做的任务
ScheduleTask是任务计划类,提供需要做计划任务的数据
Task是任务执行结果类,实现了任务的运行和运行之后的状态数据如何保存
TaskManager是核心类,主要是管理任务,提供初始化和开始方法,维护TaskThread列表
TaskService是ITaskService的实现,实现具体的任务执行的方法
TaskThread是核心类,主要是定义定时器和维护任务列表
IScheduleTaskService:
public partial interface IScheduleTaskService { /// <summary> /// 删除任务 /// </summary> /// <param name="task"></param> void DeleteTask(ScheduleTask task); /// <summary> /// 根据ID获取任务 /// </summary> /// <param name="taskId">主键</param> /// <returns></returns> ScheduleTask GetTaskById(int taskId); /// <summary> /// 根据类型获取任务 /// </summary> /// <param name="type">任务类型</param> /// <returns></returns> ScheduleTask GetTaskByType(string type); /// <summary> /// 获取所有任务 /// </summary> /// <param name="showHidden">是否显示隐藏的任务</param> /// <returns>Tasks</returns> IList<ScheduleTask> GetAllTasks(bool showHidden = false); /// <summary> /// 增加一个任务 /// </summary> /// <param name="task"></param> void InsertTask(ScheduleTask task); /// <summary> /// 更新一个任务 /// </summary> /// <param name="task"></param> void UpdateTask(ScheduleTask task); }
ITask:
public partial interface ITask { /// <summary> /// 执行任务 /// </summary> void Execute(); }
ITaskService:
public interface ITaskService { ITask CreateTask(string taskType); }
ScheduleTaskService:
public partial class ScheduleTaskService : IScheduleTaskService { #region Fields private readonly IRepository<ScheduleTask> _taskRepository; #endregion #region Ctor public ScheduleTaskService(IRepository<ScheduleTask> taskRepository) { this._taskRepository = taskRepository; } #endregion #region Methods public virtual void DeleteTask(ScheduleTask task) { if (task == null) throw new ArgumentNullException("task"); _taskRepository.Delete(task); } public virtual ScheduleTask GetTaskById(int taskId) { if (taskId == 0) return null; return _taskRepository.GetById(taskId); } public virtual ScheduleTask GetTaskByType(string type) { if (String.IsNullOrWhiteSpace(type)) return null; var query = _taskRepository.Table; query = query.Where(st => st.Type == type); query = query.OrderByDescending(t => t.ID); var task = query.FirstOrDefault(); return task; } public virtual IList<ScheduleTask> GetAllTasks(bool showHidden = false) { var query = _taskRepository.Table; if (!showHidden) { query = query.Where(t => t.Enabled); } query = query.OrderByDescending(t => t.Seconds); var tasks = query.ToList(); return tasks; } public virtual void InsertTask(ScheduleTask task) { if (task == null) throw new ArgumentNullException("task"); _taskRepository.Insert(task); } public virtual void UpdateTask(ScheduleTask task) { if (task == null) throw new ArgumentNullException("task"); _taskRepository.Update(task); } #endregion }
Task:
public class Task { #region Ctor /// <summary> /// 初始化新实例 /// </summary> /// <param name="task"></param> public Task(ScheduleTask task) { this.Type = task.Type; this.Enabled = task.Enabled; this.StopOnError = task.StopOnError; this.Name = task.Name; } /// <summary> /// 创建默认实例 /// </summary> private Task() { this.Enabled = true; } #endregion #region Properties /// <summary> /// 是否正在运行 /// </summary> public bool IsRunning { get; private set; } /// <summary> /// 任务最后开始时间 /// </summary> public DateTime? LastStart { get; private set; } /// <summary> /// 任务最后结束时间 /// </summary> public DateTime? LastEnd { get; private set; } /// <summary> /// 任务最后执行成功时间 /// </summary> public DateTime? LastSuccess { get; private set; } /// <summary> /// 任务类型 /// </summary> public string Type { get; private set; } /// <summary> /// 是否因为发生错误而停止 /// </summary> public bool StopOnError { get; private set; } /// <summary> /// 任务名称 /// </summary> public string Name { get; private set; } /// <summary> /// 是否启用 /// </summary> public bool Enabled { get; set; } #endregion #region Methods /// <summary> /// The execute. /// 执行任务 /// </summary> /// <param name="throwException"> /// The throw exception. /// </param> /// <param name="dispose"> /// The dispose. /// </param> public void Execute(bool throwException = false, bool dispose = true) { this.IsRunning = true; IRepository<ScheduleTask> _taskRepo = new EFRepository<ScheduleTask>(); var query = _taskRepo.Table; query = query.Where(st => st.Type == this.Type); query = query.OrderByDescending(t => t.ID); var scheduleTask = query.FirstOrDefault(); try { ITaskService taskService = new TaskService(); ITask task = this.CreateTask(this.Type, taskService); if (task != null) { this.LastStart = DateTime.Now; if (scheduleTask != null) { scheduleTask.LastStartDate = this.LastStart; // 更新任务计划执行情况 //scheduleTaskService.Update(scheduleTask); } task.Execute(); this.LastEnd = this.LastSuccess = DateTime.UtcNow; } } catch (Exception exc) { this.Enabled = !this.StopOnError; this.LastEnd = DateTime.UtcNow; } if (scheduleTask != null) { scheduleTask.LastEndDate = this.LastEnd; scheduleTask.LastSuccessDate = this.LastSuccess; // 更新任务计划执行情况 //scheduleTaskService.Update(scheduleTask); } this.IsRunning = false; } private ITask CreateTask(string taskType, ITaskService taskService) { if (taskService == null) { throw new ArgumentNullException("taskService"); } ITask task = null; if (this.Enabled) { task = taskService.CreateTask(taskType); } return task; } #endregion }
TaskManager:
public class TaskManager { #region Fields /// <summary> /// 任务管理 /// </summary> private static readonly TaskManager taskManager = new TaskManager(); /// <summary> /// 任务线程 /// </summary> private readonly List<TaskThread> taskThreads = new List<TaskThread>(); /// <summary> /// 任务运行间隔时间 /// </summary> private const int NotRunTasksInterval = 60 * 30; // 30 minutes #endregion #region Ctor /// <summary> /// /// </summary> private TaskManager() { } #endregion #region Properties /// <summary> /// 获取任务实例 /// </summary> public static TaskManager Instance { get { return taskManager; } } /// <summary> /// 任务线程列表 /// </summary> public IList<TaskThread> TaskThreads { get { return new ReadOnlyCollection<TaskThread>(this.taskThreads); } } #endregion #region Methods /// <summary> /// 这里读取所有的计划任务,并对计划任务按照运行时间间隔分组,每组的任务创建一个TaskThread /// </summary> public void Initialize() { this.taskThreads.Clear(); IRepository<ScheduleTask> _taskRepo = new EFRepository<ScheduleTask>(); // 按运行时间间隔分组 var scheduleTasks = _taskRepo.Table.OrderBy(x => x.Seconds).ToList(); // 按运行时间间隔分组 foreach (var scheduleTaskGrouped in scheduleTasks.GroupBy(x => x.Seconds)) { // 创建一个线程 var taskThread = new TaskThread { Seconds = scheduleTaskGrouped.Key }; foreach (var scheduleTask in scheduleTaskGrouped) { var schedTask = new ScheduleTask { Type = scheduleTask.Type, StopOnError = scheduleTask.StopOnError, Seconds = scheduleTask.Seconds, Name = scheduleTask.Name, LastSuccessDate = scheduleTask.LastSuccessDate, LastStartDate = scheduleTask.LastStartDate, LastEndDate = scheduleTask.LastEndDate, Enabled = scheduleTask.Enabled, }; var task = new Task(schedTask); taskThread.AddTask(task); } this.taskThreads.Add(taskThread); } // 有时一个任务周期可以被设置为几个小时(甚至几天)。 // 在这种情况下的概率,运行是相当少的(一个应用程序可以被重新启动) // 我们应该手动运行没有长时间运行的任务 var notRunTasks = scheduleTasks // 找到“运行周期”30分钟以上的任务 .Where(x => x.Seconds >= NotRunTasksInterval) .Where(x => !x.LastStartDate.HasValue || x.LastStartDate.Value.AddSeconds(x.Seconds) < DateTime.Now) .ToList(); //为不是长时间运行的任务创建一个线程 if (notRunTasks.Count > 0) { var taskThread = new TaskThread { RunOnlyOnce = true, Seconds = 60 * 5 //在程序启动以后5分钟开始运行程序 }; foreach (var scheduleTask in notRunTasks) { var schedTask = new ScheduleTask { Type = scheduleTask.Type, StopOnError = scheduleTask.StopOnError, Seconds = scheduleTask.Seconds, Name = scheduleTask.Name, LastSuccessDate = scheduleTask.LastSuccessDate, LastStartDate = scheduleTask.LastStartDate, LastEndDate = scheduleTask.LastEndDate, Enabled = scheduleTask.Enabled, }; var task = new Task(schedTask); taskThread.AddTask(task); } this.taskThreads.Add(taskThread); } } public void Start() { foreach (var taskThread in this.taskThreads) { taskThread.InitTimer(); } } public void Stop() { foreach (var taskThread in this.taskThreads) { taskThread.Dispose(); } } #endregion }
TaskService:
public class TaskService : ITaskService { public ITask CreateTask(string taskType) { switch (taskType) { case "sendemall": //执行操作 string aa = "123"; break; case "huilv": // string aa1 = "123"; break; case "qichuang": string aa2 = "123"; break; } return null; } }
TaskThread:
public partial class TaskThread : IDisposable { private Timer _timer; private bool _disposed; private readonly Dictionary<string, Task> _tasks; internal TaskThread() { this._tasks = new Dictionary<string, Task>(); this.Seconds = 10 * 60; } private void Run() { if (Seconds <= 0) return; this.StartedUtc = DateTime.UtcNow; this.IsRunning = true; foreach (Task task in this._tasks.Values) { task.Execute(); } this.IsRunning = false; } private void TimerHandler(object state) { this._timer.Change(-1, -1); this.Run(); if (this.RunOnlyOnce) { this.Dispose(); } else { this._timer.Change(this.Interval, this.Interval); } } /// <summary> /// 部署实例 /// </summary> public void Dispose() { if ((this._timer != null) && !this._disposed) { lock (this) { this._timer.Dispose(); this._timer = null; this._disposed = true; } } } /// <summary> /// /// </summary> public void InitTimer() { if (this._timer == null) { this._timer = new Timer(new TimerCallback(this.TimerHandler), null, this.Interval, this.Interval); } } /// <summary> /// 添加一个任务线程 /// </summary> /// <param name="task"></param> public void AddTask(Task task) { if (!this._tasks.ContainsKey(task.Name)) { this._tasks.Add(task.Name, task); } } /// <summary> /// 其中运行任务的间隔秒数 /// </summary> public int Seconds { get; set; } public DateTime StartedUtc { get; private set; } public bool IsRunning { get; private set; } public IList<Task> Tasks { get { var list = new List<Task>(); foreach (var task in this._tasks.Values) { list.Add(task); } return new ReadOnlyCollection<Task>(list); } } public int Interval { get { return this.Seconds * 1000; } } public bool RunOnlyOnce { get; set; } }
很重要的是要在Global中添加:
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { TaskManager.Instance.Initialize(); TaskManager.Instance.Start(); //*** } }
顺便附上数据库:
SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[ScheduleTask]( [ID] [int] IDENTITY(1,1) NOT NULL, [Name] [varchar](50) NOT NULL, [Seconds] [int] NOT NULL, [Type] [varchar](500) NOT NULL, [Enabled] [bit] NOT NULL, [StopOnError] [bit] NOT NULL, [CreatDate] [datetime] NOT NULL, [LastStartDate] [datetime] NULL, [LastEndDate] [datetime] NULL, [LastSuccessDate] [datetime] NULL, CONSTRAINT [PK_ScheduleTask] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO SET ANSI_PADDING OFF GO
9 发送邮件 20 sendemall True False 2015-07-03 09:30:51.483 2015-07-03 09:30:51.483 2015-07-03 09:30:51.483 2015-07-03 09:30:51.483 10 获取汇率 20 huilv True False 2015-07-03 09:30:51.483 2015-07-03 09:30:51.483 2015-07-03 09:30:51.483 2015-07-03 09:30:51.483 11 提醒起床 30 qichuang True False 2015-07-03 09:30:51.483 2015-07-03 09:30:51.483 2015-07-03 09:30:51.483 2015-07-03 09:30:51.483
运行结果,可以看到,每20秒的时候,会执行发送邮件跟获取汇率。每30秒会执行提醒起床。

浙公网安备 33010602011771号