C#一個簡單的定時表達式(HH:mm:ss)解析


前言

  為客戶開發了一個日志監控程序,監聽各頻道是否正常工作。其中有一個功能是這樣的,當所有頻道正常運行一段時間后,語音提示值班人員系統運行正常。一開始,想法比較簡單,設置了一個變量,在線程不斷輪詢的過程中去統計連續正常運行的總時長,當達到設置的閥值后,提交一條語音播報任務。后來,客戶反饋他們需要定點去值班,順道查看下軟件是否正常,聽聽語音播報。一溝通,好吧,做個類似於cron表達式那樣的定時吧,按照設定的時間規則進行語音播報控制。下面寫寫我的簡單示例,聊以自娛。

一、設置一個定時的配置項

<!-- 系統正常運行定時任務,*:/1:00 表示每小時 每隔1分的時候報-->
    <SysNormalLong>*:00:00</SysNormalLong>
  • 示例1【 *:00:00 】,*表示每小時都執行,分鍾配置成*,則表示每小時都執行。該示例意思是 每小時都整點播報;
  • 示例2【 *:/15:00】, /表示配置的是間隔。該示例意思是 每隔15分鍾播報一次;
  • 示例3【 12:20:00】,這個意思是每天 12:20:00 播報一次;

二、解析定時配置,生成時分秒對應的驗證函數

    private string _sysNormalLong = @"*:00:00";
        /// <summary>
        /// 正常播報時分秒
        /// </summary>
        private string[] _normals;

        private Func<int, bool>[] _normalFuncs = new Func<int, bool>[3];

  /// <summary>
        /// 單項驗證
        /// </summary>
        /// <param name="val"></param>
        /// <returns></returns>
        public Func<int,bool> GetValidFunc(string val)
        {
            if (val == "*") return t=>true;
            int temp = 0;
            if (val.StartsWith("/"))
            {
                //按間隔播報
                if (Int32.TryParse(val.Replace("/", ""), out temp))
                {
                    return t=> (t%temp==0);
                }
            }
            else
            {
                //定點播報
                if (Int32.TryParse(val, out temp))
                {
                    return t=>t==temp;
                }
            }
            //解析失敗,則按照整點播報的邏輯來做
            return t=>t==0;
        }

  private SoundWarnThread()
        {
            ……
            _normals = _sysNormalLong.Split(':');
            _normalFuncs[0] = GetValidFunc(_normals[0]);
            _normalFuncs[1] = GetValidFunc(_normals[1]);
            _normalFuncs[2] = GetValidFunc(_normals[2]);
            _nextNormalTime = GetNextNormalTime(DateTime.Now.AddSeconds(20));
            ……
        }

  主要方法為GetValidFunc。改方法為時分秒每個時間部分都生成一個匿名的判斷函數,確保對配置項做一次解析,避免后期在驗證過程中不斷去分析配置字符串,提升性能。判斷邏輯為:1、當配置*,則都驗證為true;2、當配置以/開頭,則說明配置的是間隔時間,這時從0開始計算,若當前時間是間隔時間的整數倍,則驗證為true;3、配置的是純數字則是固定時間,直接比對是否相等即可。

三、傳入一個時間,驗證當前時間是否符合定時規則

public bool ValidNormal(DateTime now)
        {
            //變動快的部分先驗證,代碼執行速度快
            bool bS = _normalFuncs[2](now.Second); 
            if (!bS) return false;
            bool bM = _normalFuncs[1](now.Minute); 
            if (!bM) return false;
            bool bH = _normalFuncs[0](now.Hour);
            if (!bH) return false;
            return true;
        }

四、獲取最近一次即將到達的定時時間

/// <summary>
        /// 獲取下次正常播報的時間
        /// </summary>
        /// <returns></returns>
        public DateTime GetNextNormalTime(DateTime date)
        {
            while (!ValidNormal(date))
            {
                date = date.AddSeconds(1);
            }
            return date;
        }

五、判斷某一個時間是否該進行語音播報了(這種方案主要是為了防止線程輪詢時執行時間過長,錯過定時任務執行時間)

public bool VoiceNormal(DateTime now)
        {
            if (now >= _nextNormalTime)
            {
                _nextNormalTime = GetNextNormalTime(now.AddSeconds(1));
                //如果超過1分鍾的偏差都沒能報出正常運行,則認為軟件在這個時間段內捕獲到了異常,丟棄這一次的正常運行播報
                if (now - _nextNormalTime > TimeSpan.FromMinutes(1)) return false;
                _lastNormalTime = now;
                return true;
            }
            return false;
        }

六、定義一個調用線程

#region 線程處理

        private Thread _mainThread = null;
        public void Start()
        {
            _mainThread = new Thread(new ThreadStart(MainProcess));
            _mainThread.IsBackground = true;
            _mainThread.Start();
        }
        
        /// <summary>
        /// 正常運行的提示信息
        /// </summary>
        private const string _normalMsg = "播出系統日志監聽正常";

        private DateTime? _nextNormalTime;
        /// <summary>
        /// 主要的處理邏輯
        /// </summary>
        private void MainProcess()
        {
            //1秒讀取一次
            TimeSpan interval = TimeSpan.FromSeconds(1);
            while (true)
            {
                if (!WarningQueue.Any())
                {
                    Thread.Sleep(interval);
                    if (VoiceNormal(DateTime.Now))
                    {
                        Voice(_normalMsg);
                    }
                    continue;
                }
                ……
            }
        }

 


免責聲明!

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



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