多任務定時器


   來到園子有些時間了,一直關注着園子里大牛的帖子。作為一個走人社會2年的猿,也一直幻想着有一天能技術牛一回,升職加薪。春節快到了,老板也不給發年終,尾牙也沒有,連出差的報銷一直卡在老板那一個多月了也沒有結果。這些就都不說了,這些都不是重點。重點是今天是鄙人博客園開篇的日子,分享一下自己的想法,願大家多多指教。

   最近,公司正在開發智能家居項目。作為還是一名涉世不深的猿,還沒有什么資質討論架子,這種高大上的問題,就討論一下小功能的實現。

  • 場景再現:

在智能家居項目中,經常有定時檢查家居狀態的需求。如:凈水器濾芯過期警告。要求每天12:00檢測,所有再用凈水器濾芯使用時長。如果過期,就向用戶手機推送濾芯過期警告。

  • 功能需求
  1. 在此將每個定時處理動作叫做任務(Task)
  2. 由於項目中這樣的任務不唯一,要允許多任務定時執行
  3. 任務可以通過簡單配置便可以運行,配置中指定任務執行內容,定時信息
  • 設計思路
  1. 用兩條線程來實現這樣的功能,1條線程充當調度者(Dispatcher),1條線程充當執行者(Executor)。
  2. 調度者循環獲取當前執行的任務,交給執行者執行。
  3. 任務按當前任務執行時間排序,調度者每次調度第一個任務,修改任務最后執行時間,從而修改下次循環任務執行時間。
  • 實現
  1.   ITask 接口,任務具體實現需繼承該接口
    namespace IHUserService.TaskExecutor
    {
        public interface ITask
        {
            void Execute(object state);
        }
    }

     

  2. TaskContent 包含一些任務執行所需的信息
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    
    namespace IHUserService.TaskExecutor
    {
        [Serializable]
        public partial class TaskContent : IComparable, ICloneable
        {
            private string[] partialExecuteTimes;
            private List<DateTime> tempExecuteTimes = new List<DateTime>();
            public DateTime? StartTime { get; set; }
    
            public DateTime? LastStartTime { get; set; }
    
            public TimeSpan Interval { get; set; }
    
            public bool IsRepeat { get; set; }
    
            public ITask Task { get; private set; }
    
            public DateTime? CurrentExecuteTime { get; set; }
            public string ExcuteTimeFormat { get; set; }
    
            private string assemblyType;
            public string AssemblyType
            {
                get
                {
                    return assemblyType;
                }
                set
                {
                    assemblyType = value;
                    string[] assembly = value.Split(',');
                    Task = Assembly.Load(assembly[1]).CreateInstance(assembly[0]) as ITask;
                }
            }
    
            private string excuteTime;
            public string ExcuteTime
            {
                get
                {
                    return excuteTime;
                }
                set
                {
                    excuteTime = value;
                    partialExecuteTimes = excuteTime.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                }
            }
    
    
    
            public int CompareTo(object obj)
            {
                var nextTask = obj as TaskContent;
                DateTime t1 = GetExcuteTime();
                DateTime t2 = nextTask.GetExcuteTime();
                return t1.CompareTo(t2);
            }
    
            public DateTime GetExcuteTime()
            {
                return GetNextExcuteTime(false);
            }
            public DateTime GetNextExcuteTime(bool removeTemp = true)
            {
                if (StartTime.HasValue && Interval != null)
                {
                    return LastStartTime.HasValue ? LastStartTime.Value.Add(Interval) : StartTime.Value;
                }
                else if (!string.IsNullOrWhiteSpace(ExcuteTimeFormat) && !string.IsNullOrWhiteSpace(ExcuteTime))
                {
                    if (tempExecuteTimes.Count == 0)
                    {
                        LastStartTime = LastStartTime.HasValue ? LastStartTime.Value : DateTime.Now;
                        int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
                        int incrementYear = 0, incrementMonth = 0, incrementDay = 0, incrementHour = 0, incrementMinute = 0;
    
                        foreach (var value in partialExecuteTimes)
                        {
                            switch (ExcuteTimeFormat)
                            {
                                case "MMdd hh:mm:ss"://每年的MM月1日 00時 00分 00 秒執行
                                    year = LastStartTime.Value.Year;
                                    month = Convert.ToInt32(value.Substring(0, 2));
                                    day = Convert.ToInt32(value.Substring(2, 2));
                                    hour = Convert.ToInt32(value.Substring(5, 2));
                                    minute = Convert.ToInt32(value.Substring(8, 2));
                                    second = Convert.ToInt32(value.Substring(11, 2));
                                    incrementYear = 1;
                                    break;
                                case "dd hh:mm:ss":
                                    year = LastStartTime.Value.Year;
                                    month = LastStartTime.Value.Month;
                                    day = Convert.ToInt32(value.Substring(0, 2));
                                    hour = Convert.ToInt32(value.Substring(3, 2));
                                    minute = Convert.ToInt32(value.Substring(6, 2));
                                    second = Convert.ToInt32(value.Substring(9, 2));
                                    incrementMonth = 1;
                                    break;
                                case "hh:mm:ss":
                                    year = LastStartTime.Value.Year;
                                    month = LastStartTime.Value.Month;
                                    day = LastStartTime.Value.Day;
                                    hour = Convert.ToInt32(value.Substring(0, 2));
                                    minute = Convert.ToInt32(value.Substring(3, 2));
                                    second = Convert.ToInt32(value.Substring(6, 2));
                                    incrementDay = 1;
                                    break;
                                case "mm:ss":
                                    year = LastStartTime.Value.Year;
                                    month = LastStartTime.Value.Month;
                                    day = LastStartTime.Value.Day;
                                    hour = LastStartTime.Value.Hour;
                                    minute = Convert.ToInt32(value.Substring(0, 2));
                                    second = Convert.ToInt32(value.Substring(3, 2));
                                    incrementHour = 1;
                                    break;
                                case "ss":
                                    year = LastStartTime.Value.Year;
                                    month = LastStartTime.Value.Month;
                                    day = LastStartTime.Value.Day;
                                    hour = LastStartTime.Value.Hour;
                                    minute = LastStartTime.Value.Minute;
                                    second = Convert.ToInt32(value.Substring(0, 2));
                                    incrementMinute = 1;
                                    break;
                                default:
                                    throw new Exception("IHUserService.TaskExecutor 配置錯誤!");
                            }
                            var dt = new DateTime(year, month, day, hour, minute, second);
                            if (LastStartTime.HasValue && dt.CompareTo(LastStartTime.Value) < 1)
                            {
                                dt = dt.AddYears(incrementYear);
                                dt = dt.AddMonths(incrementMonth);
                                dt = dt.AddDays(incrementDay);
                                dt = dt.AddHours(incrementHour);
                                dt = dt.AddMinutes(incrementMinute);
                            }
                            tempExecuteTimes.Add(dt);
                        }
                    }
                    tempExecuteTimes.Sort();
                    var result = tempExecuteTimes.FirstOrDefault();
                    if (removeTemp)
                    {
                        tempExecuteTimes.Remove(result);
                        CurrentExecuteTime = result;
                    }
                    return result;
                }
                else
                {
                    throw new Exception("IHUserService.TaskExecutor 配置錯誤!");
                }
            }
    
            public object Clone()
            {
                return new TaskContent()
                {
                    AssemblyType = this.AssemblyType,
                    CurrentExecuteTime = this.CurrentExecuteTime,
                    ExcuteTime = this.ExcuteTime,
                    ExcuteTimeFormat = this.ExcuteTimeFormat,
                    Interval = this.Interval,
                    IsRepeat = this.IsRepeat,
                    LastStartTime = this.LastStartTime,
                    partialExecuteTimes = this.partialExecuteTimes,
                    StartTime = this.StartTime,
                    tempExecuteTimes = this.tempExecuteTimes
                };
            }
        }
    }

    3.TaskCollection 

    using RestSharp.Serializers;
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.IO;
    using System.Linq;
    using System.Text;
    
    namespace IHUserService.TaskExecutor
    {
        [Serializable]
        public class TaskCollection : List<TaskContent>
        {
            public TaskContent GetNextTask() 獲取下次執行的任務
            {
                this.Sort();
                Current = this[0];
                if (!Current.IsRepeat)
                {
                    this.Remove(Current);
                }
                return Current;
            }
    
            public TaskContent Current { get; set; }   //當前執行的任務
    
            public void LoadXmlConfigAppandToList(string file)  //加載Xml配置
            {
                var xs = new System.Xml.Serialization.XmlSerializer(typeof(TaskCollection));
                StreamReader sr = new StreamReader(file);
                var config = xs.Deserialize(sr) as TaskCollection;
                sr.Close();
                foreach (var item in config)
                {
                    Add(item);
                }
            }
    
            public void LoadAppConfigAppandToList() //加載Congfig 配置
            {
                var values = ConfigurationManager.AppSettings["TaskExecutor.TaskCollection"].Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (var str in values)
                {
                    var settings = ConfigurationManager.AppSettings["TaskExecutor.TaskContent." + str].Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                    var task = new TaskContent() { ExcuteTime = settings[1], AssemblyType = settings[2] + "," + settings[3], ExcuteTimeFormat = settings[0], IsRepeat = true };
                    Add(task);
                }
            }
        }
    }

4.TaskCenter

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace IHUserService.TaskExecutor
{
    public class TaskCenter
    {
        public TaskCollection Tasks { get; set; }

        public bool IsRun { get; set; }
        public void ExecuteTasks()//啟動任務執行器
        {
            Tasks = new TaskCollection();
            Tasks.LoadAppConfigAppandToList();
            IsRun = true;
            ThreadPool.QueueUserWorkItem(new WaitCallback(Dispatcher), null);
        }

        public void Stop()
        {
            IsRun = false;
        }
        private void Dispatcher(object state) //處理調度
        {
            var ts0 = new TimeSpan(0, 0, 0, 0, 0);
            while (IsRun)
            {
                Tasks.GetNextTask();
                var content = Tasks.Current;
                var taskTime = content.GetNextExcuteTime();
                content.LastStartTime = taskTime;
                var ts = taskTime - DateTime.Now;
                if (ts > ts0)
                {
                    Thread.Sleep(ts);
                }
                ThreadPool.QueueUserWorkItem(new WaitCallback(content.Task.Execute), Tasks.Current.Clone());//執行任務
            }
        }
    }
}

5.調用

    class Program
    {
        static void Main(string[] args)
        {
            TaskCenter center = new TaskCenter();
            center.ExecuteTasks();
            Console.ReadLine();
        }
    }

    public class TestTask : ITask
    {
        string id = Guid.NewGuid().ToString();
        public void Execute(object state)
        {
            var a = state as TaskContent;
            Console.WriteLine(id + "本次預定執行時間:" + a.CurrentExecuteTime.Value.ToString("yyyy-MM-dd HH:mm:ss.ffffzzz"));
            Console.WriteLine(id + "當前系統時間    :" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffffzzz"));
        }
    }

 

Config配置

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <clear/>
    <add key="TaskExecutor.TaskCollection" value="testtask,0003"/>
    <add key="TaskExecutor.TaskContent.testtask" value="ss,00|00|00|07|10|30,ConsoleApplication1.TestTask,ConsoleApplication1"/>
    <add key="TaskExecutor.TaskContent.0003" value="ss,05,ConsoleApplication1.TestTask,ConsoleApplication1"/>
  </appSettings>
</configuration>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM