可重用的定时任务实现
配置化的定时任务实现
在一个项目中,经常会在到后台执行某一计划,如每隔几分钟检查磁盘信息,定时发邮件,监视某一值的状态等 .
那么经常用到这样的功能,如何让它尽可能的重用化呢
思考一下,任务以配置文件形式添加 ,在程序的启动点启动任务. 按照配置文件里的程序信息, 执行入口定时反复执行。
配置文件如下定义:
<?xml version="1.0" encoding="utf-8"?> <Tasks> <Modules> <add name="TestTaskModule" type="TestTask.TaskModule, TestTask" /> </Modules> <Threads> <Thread minutes="0" seconds="1"> <task name = "test1" type ="TestTask.Test, TestTask" enabled = "true" enableShutDown = "false" failureInterval = "1" numberOfTries = "100" numberOfQueue="20" /> <task name = "test2" type ="TestTask.Test, TestTask" enabled = "true" enableShutDown = "false" failureInterval = "1" numberOfTries = "100" numberOfQueue="20" /> </Thread> <Threads> </Tasks>
Thread节点,配置执行任务的间隔
首先 ,我们需要定义二个接口 ,在你的定时任务中实现接口
public interface ITask { void Execute(XmlNode node); } public interface ITaskModule { void Init(TaskApplication taskApplication, XmlNode node); }
ITaskModule不是必须,只是在你需要处理任务中的异常,及监视任务执行情况才需要用到它
定义一个任务管理的类
TaskManager.cs
一个线程类
TaskThread.cs
任务类
Task.cs
public sealed class TaskManager { List<TaskThread> taskThreads = new List<TaskThread>(); static readonly TaskManager taskManager = new TaskManager(); private TaskManager() { } public static TaskManager Initialize(string configFile) { return Initialize(configFile, "Tasks"); } public static TaskManager Initialize(XmlNode node) { taskManager.Analyze(node); return taskManager; } public static TaskManager Initialize(string configFile, string nodePath) { XmlDocument document = new XmlDocument(); document.Load(configFile); return Initialize(document.SelectSingleNode(nodePath)); } public IList<TaskThread> TaskThreads { get { return (IList<TaskThread>)new ReadOnlyCollection<TaskThread>((IList<TaskThread>)this.taskThreads); } } public static TaskManager Instance() { return TaskManager.taskManager; } public bool IsTaskEnabled(string taskName) { foreach (TaskThread thread in this.taskThreads) { if (thread.GetTasks().ContainsKey(taskName)) { return thread.GetTasks()[taskName].Enabled; } } return false; } public void Start() { TaskApplication.GetTaskApplication().FireTaskStartup(); foreach (TaskThread thread in this.taskThreads) { thread.Start(); } } public void Stop() { foreach (TaskThread thread in this.taskThreads) { thread.Dispose(); } this.taskThreads.Clear(); TaskApplication.GetTaskApplication().FireTaskShutdown(); } public IList<TaskThread> Threads { get { return new ReadOnlyCollection<TaskThread>(this.taskThreads); } } void Analyze(XmlNode node) { foreach (XmlNode xmlNode in node.ChildNodes) { string str; if ((str = xmlNode.Name.ToLower()) != null) { if (!(str == "modules")) { this.AddModules(xmlNode); } if (str == "threads") this.AddThreads(xmlNode); } } } void AddModules(XmlNode node2) { TaskApplication taskApplication = TaskApplication.GetTaskApplication(); foreach (XmlNode node in node2.ChildNodes) { string name; if (node.NodeType != XmlNodeType.Comment && (name = node.Name) != null) { if (name =="clear") { taskApplication.GetTaskModules().Clear(); } if (name == "remove") { XmlAttribute nameAttr = node.Attributes["name"]; string key = nameAttr == null ? (string)null : nameAttr.Value; if (!string.IsNullOrEmpty(key) && taskApplication.GetTaskModules().ContainsKey(key)) taskApplication.GetTaskModules().Remove(key); } if (name == "add") { XmlAttribute attr = node.Attributes["enabled"]; if (attr == null || !(attr.Value == "false")) { XmlAttribute nameAttr = node.Attributes["name"]; XmlAttribute typeAttr = node.Attributes["type"]; string key = nameAttr == null ? (string)null : nameAttr.Value; string typeName = typeAttr == null ? (string)null : typeAttr.Value; if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(typeName)) { Type type = Type.GetType(typeName); if (type != null) { ITaskModule taskModule = Activator.CreateInstance(type) as ITaskModule; if (taskModule != null) { taskModule.Init(taskApplication, node); taskApplication.GetTaskModules().Add(key, taskModule); } } } } } } } } void AddThreads(XmlNode node) { this.taskThreads.Clear(); foreach (XmlNode child in node.ChildNodes) { if (child.Name.ToLower() == "thread") { TaskThread taskThread = new TaskThread(child); this.taskThreads.Add(taskThread); foreach (XmlNode node2 in child.ChildNodes) { if (node2.Name.ToLower() == "task") { Type type = Type.GetType(node2.Attributes["type"].Value); if (type != null) { Task task = new Task(type, node2); taskThread.AddTask(task); } } } } } } }
public sealed class TaskThread : IDisposable { Dictionary<string, Task> tasks; bool isRunning; bool dispose; DateTime started; DateTime created; DateTime completed; int firstRun; int minutes; int seconds; Timer timer; private TaskThread() { this.tasks = new Dictionary<string, Task>(); this.seconds = 15; } internal TaskThread(XmlNode node) { this.tasks = new Dictionary<string, Task>(); this.seconds = 15; this.started= DateTime.Now; this.isRunning = false; if ((node.Attributes["minutes"] != null) && !int.TryParse(node.Attributes["minutes"].Value, out this.minutes)) { this.minutes = 15; } if ((node.Attributes["seconds"] != null) && !int.TryParse(node.Attributes["seconds"].Value, out this.seconds)) { this.seconds = 0; } if ((node.Attributes["firstRun"] != null) && !int.TryParse(node.Attributes["firstRun"].Value, out this.firstRun)) { this.firstRun = 0; } } public void Dispose() { if ((this.timer != null) && !this.dispose) { lock (this) { this.timer.Dispose(); this.timer = null; this.dispose = true; } } } public Dictionary<string, Task> GetTasks() { return this.tasks; } public void Start() { if (this.timer == null) { this.timer = new Timer(new TimerCallback(this.TimerHandler), null, this.Interval, this.Interval); } } void run() { this.started = DateTime.Now; this.isRunning= true; TaskApplication.GetTaskApplication().FirePreTaskRun(); foreach (Task task in this.tasks.Values) task.Run(); TaskApplication.GetTaskApplication().FirePostTaskRun(); this.dispose = false; this.completed = DateTime.Now; } void TimerHandler(object state) { this.timer.Change(-1, -1); this.firstRun= -1; this.run(); this.timer.Change(this.Interval, this.Interval); } public void AddTask(Task task1) { if (!this.tasks.ContainsKey(task1.Name)) { this.tasks.Add(task1.Name, task1); } } public DateTime Completed { get { return this.completed; } } public DateTime Created { get { return this.created; } } public int FirstRun { get { return this.firstRun; } } public int Interval { get { if (this.seconds> 0) { return (this.seconds * 1000); } return (this.Minutes *60000); } } public bool IsRunning { get { return this.isRunning; } } public int Minutes { get { return this.minutes; } } public int Seconds { get { return this.seconds; } } public DateTime Started { get { return this.started; } } public IList<Task> Tasks { get { List<Task> list = new List<Task>(); foreach (Task task in this.tasks.Values) list.Add(task); return new ReadOnlyCollection<Task>((IList<Task>)list); } } }
[Serializable, XmlRoot("task")] public sealed class Task { private Type jobType; private string name; private bool enableShutDown; private XmlNode node; private bool isRunning; private ITask task; private bool enable; private DateTime lastStarted; private DateTime lastSuccess; private DateTime lastEnd; private Task() { this.enable = true; } internal Task(Type ijob, XmlNode node) { this.enable = true; this.node = node; this.jobType = ijob; if ((node.Attributes["enable"] != null) && !bool.TryParse(node.Attributes["enable"].Value, out this.enable)) { this.enable = true; } if ((node.Attributes["enableShutDown"] != null) && !bool.TryParse(node.Attributes["enableShutDown"].Value, out this.enableShutDown)) { this.enableShutDown = true; } if (node.Attributes["name"] != null) { this.name = node.Attributes["name"].Value; } } public void Run() { this.isRunning = true; try { ITask task = this.getTask(); if (task != null) { this.lastStarted = DateTime.Now; task.Execute(this.node); this.lastEnd = this.lastSuccess = DateTime.Now; } } catch (Exception exception) { this.enable = !this.EnableShutDown; this.lastEnd = DateTime.Now; TaskApplication.GetTaskApplication().Error(this, exception); } this.isRunning = false; } ITask getTask() { if (this.Enabled && (this.task == null)) { if (this.jobType != null) { this.task = Activator.CreateInstance(this.jobType) as ITask; } this.enable = this.task != null; } return this.task; } public bool Enabled { get { return this.enable; } } public bool EnableShutDown { get { return this.enableShutDown; } } public bool IsRunning { get { return this.isRunning; } } public Type JobType { get { return this.jobType; } } public DateTime LastEnd { get { return this.lastEnd; } } public DateTime LastStarted { get { return this.lastStarted; } } public DateTime LastSuccess { get { return this.lastSuccess; } } public string Name { get { return this.name; } } }
为何使用Timer而不是Thread, Thread并不能保证精确的时间控制 ,无法确保执行顺序,此外还有线程休眠有可能导置时间不精确的问题
测试任务
namespace TestTask { public class Test : ITask { static int i = 0; public void Execute(System.Xml.XmlNode node) { int count = 20; int.TryParse(node.Attributes["numberOfQueue"].Value, out count); Process(); } private void Process() { string test = Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName) + "\\test.txt"; string str=string.Format("{0}--{1}",i,DateTime.Now); WriteLog(test,str);//记录到文件 i++; } } } -------------------------------------------------------------- [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.ApplicationExit += new EventHandler(Application_ApplicationExit); string m_StartupPath = Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName) + "\\"; string path = m_StartupPath + "Tasks.config"; XmlNode tasksNode = null; XmlDocument doc = new XmlDocument(); doc.Load(path); tasksNode = doc.DocumentElement; //初始化任务 TaskManager.Initialize(tasksNode); //启动任务 TaskManager.Instance().Start(); Application.Run(new Form1()); } static void Application_ApplicationExit(object sender, EventArgs e) { //退出任务 TaskManager.Instance().Stop(); } }
测试结果 :
0--2012/5/21 17:14:00
1--2012/5/21 17:14:01
2--2012/5/21 17:14:02
3--2012/5/21 17:14:03
4--2012/5/21 17:14:04
5--2012/5/21 17:14:05
6--2012/5/21 17:14:06
7--2012/5/21 17:14:07
8--2012/5/21 17:14:08
9--2012/5/21 17:14:09
10--2012/5/21 17:14:10
可以用于asp.net,HttpModule的Init方法或者Globals.aspx中的Application_OnStart 中调用定时器,winform ,和service中 。
你需要做的仅是实现ITask接口,放入配置文件中 ..
当然你需要思考,在你的应用中是否真的需要定时器,例如 ,一个策略游戏,每个玩家可以创建一到多个城市,每个城市的资源增长,如果使用定时器,将是非常耗时的操作
这个时候你可以当它是薛定谔的猫.
设置一个 laskCheck 时间检查点,一个Interval检查间隔字段 ,每次取值时 对比现在时间和最后更新时间的间隔,如果大于间隔,更新laskCheck,和新数值, 小于间隔,返回数值+时间间隔和成长值
如果没有人访问,那就是薛定谔的猫. 类似 if(obj==null) obj=new Class(); 需要时在去doSomething
很久没更新博客了, 要表达的东西总是写不出来了.