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;
                }
                ……
            }
        }

 

posted @ 2018-03-01 15:57  Mr.Simm  阅读(2630)  评论(0编辑  收藏  举报