可重用的定时任务实现

 配置化的定时任务实现

   在一个项目中,经常会在到后台执行某一计划,如每隔几分钟检查磁盘信息,定时发邮件,监视某一值的状态等 .
   
   那么经常用到这样的功能,如何让它尽可能的重用化呢

   思考一下,任务以配置文件形式添加 ,在程序的启动点启动任务. 按照配置文件里的程序信息, 执行入口定时反复执行。

   配置文件如下定义:

<?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

  
  很久没更新博客了, 要表达的东西总是写不出来了.

 

posted @ 2012-05-24 15:23  潇笑  阅读(1351)  评论(4编辑  收藏  举报