C#开发系统服务时用的定时器组件

写服务时,都需要为定时器写不少的代码,感觉很麻烦,今天把这些代码封装一下,希望能简化一下这方面的工作,把精力都集中在功能上

本定时器组件,每次只启动一个服务实例进行处理,而不会同时多次执行服务代码。

下面是应用实例

从组件类派生一个子类,可以看到,需要写的代码很少

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;

namespace BlueVision.SaYuan.WinService
{
    public class TestService : ServiceTimerContorl
    {
        protected override void StartService()
        {
            //需要做的事情的代码
        }   

        protected override ServiceTimeConfig GetTimerConfig()
        {
            ServiceTimeConfig config = new ServiceTimeConfig();   

            // 如果该枚举是选择 Interval ,则表示,上述的 StartService 方法,将隔 config.ChkInterval 毫秒执行一次
            config.TimerMode = TimerMode.Interval;
            config.ChkInterval = 100000;   

            /// StartTime值为:
            ///
            /// TimerMode=TimerMode.Month   "09|04:30"  -表示每月9号的凌晨4点30分
            /// TimerMode=TimerMode.Week     "0|04:30"  -表示每星期天的4点30分
            /// TimerMode=TimerMode.Week     "6|04:00"  -表示每星期6的4点30分
            /// TimerMode=TimerMode.Day       "|04:00"  -表示每天的4点00分
            /// TimerMode=TimerMode.Date "08-10|04:00"  -表示每年8月10号的4点00分执行一次
            /// TimerMode=TimerMode.Year   "246|04"     -表示每年第246天的4点00分执行一次(可以不填写分钟默认为00)   

            //如果是这样设置 则表示,每天的凌晨 4 点 00 执行一次
            config.TimerMode = TimerMode.Day;
            config.StartTime = "|04";   

            //如果是这样设置 则表示,每个星期的星期四那天的凌晨 6 点 35 执行一次
            config.TimerMode = TimerMode.Week;
            config.StartTime = "4|06:35";   

            //如果是这样设置 则表示,每个星期的星期天那天的凌晨 6 点 00 执行一次
            config.TimerMode = TimerMode.Week;
            config.StartTime = "0|06";   

            //如果是这样设置 则表示,每个月的9号凌晨 6 点 28 执行一次
            config.TimerMode = TimerMode.Month;
            config.StartTime = "09|06:28";   

            //如果是这样设置 则表示,每年8月10号的4点00分执行一次
            config.TimerMode = TimerMode.Date;
            config.StartTime = "08-10|04:00";   

            //如果是这样设置 则表示,每年第246天的4点27分执行一次
            config.TimerMode = TimerMode.Year;
            config.StartTime = "246|04:27";   

            return config;
        }
        /// <summary>
        /// 当服务出错时,处理
        /// </summary>
        /// <param name="ex"></param>
        protected override void  ServiceException(Exception ex)
        {
             //可以不实现
        }
    }
}   

//要执行代码,以下这样就可以了
TestService testService = new TestService();
testService.Start(); 

以下是组件的源代码

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;

namespace BlueVision.SaYuan.WinService
{
    /// <summary>
    /// 服务定时器管理
    /// </summary>
    public abstract class ServiceTimerControl
    {
        #region 私有成员
        /// <summary>
        /// 定时器
        /// </summary>
        private Timer SysTimer { get; set; }
        /// <summary>
        /// 是否启用定时器
        /// </summary>
        private bool _EnabledTimer = true;
        /// <summary>
        /// 服务执行状态
        /// </summary>
        private ServiceStatus _serviceStatus = ServiceStatus.Sleep;
        /// <summary>
        /// 时间计算类
        /// </summary>
        private TimerControl _timerControl = null;
        /// <summary>
        /// 定时器配置
        /// </summary>
        ServiceTimeConfig Config;
        #endregion

        #region 公共属性
        /// <summary>
        /// 获取服务状态
        /// </summary>
        public ServiceStatus ServiceStatus { get { return _serviceStatus; } }
        /// <summary>
        /// 计时模式
        /// </summary>
        public TimerMode TimerMode
        {
            get
            {
                if ( Config == null ) Config = this.GetTimerConfig();

                return Config.TimerMode;
            }
        }
        /// <summary>
        /// 计时配置
        /// </summary>
        public string StartTime { get { return ( Config == null ) ? "" : Config.StartTime; } }
        /// <summary>
        /// 时间计算类
        /// </summary>
        public TimerControl TimerControl
        {
            get
            {
                if ( Config == null )
                    Config = this.GetTimerConfig();
                if ( _timerControl == null )
                    _timerControl = new TimerControl( this.Config.StartTime, this.Config.TimerMode );

                return _timerControl;
            }
        }
        #endregion

        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            _EnabledTimer = false;

            SysTimer.Change( Timeout.Infinite, Timeout.Infinite );
        }
        /// <summary>
        /// 开始服务
        /// </summary>
        public void Start()
        {
            _EnabledTimer = true;
            Config = this.GetTimerConfig();

            SysTimer = new Timer( new TimerCallback( this.TimerProcess ), AppDomain.CurrentDomain, 0, this.Config.ChkInterval );
        }
        /// <summary>
        /// 处理间隔服务
        /// </summary>
        /// <param name="sender"></param>
        private void TimerProcess( object sender )
        {
            if ( !_EnabledTimer ) return;

            bool TimeIsUp = true;
            if ( this.Config.TimerMode != TimerMode.Interval )
            {
                // 如果定时方式不是定时轮询的话,就构造TimerControl类,该类用来计算每次执行完程序后
                // 到下次执行服务时需要休眠的时间
                try
                {
                    _timerControl = new TimerControl( this.Config.StartTime, this.Config.TimerMode );
                    TimeIsUp = _timerControl.TimeIsUp;    // 获取是否到了执行服务程序的时间了
                }
                catch ( Exception ex )
                {
                    // 读取配置出错且TimerControl对象已不存在,则再抛出异常
                    // 如果上一次读取配置成功,那就就算这次的配置有问题,则也不会停止程序的运行,仍用上一次的数据做为参数
                    if ( _timerControl == null ) throw ex;
                }
            }

            try
            {
                if ( TimeIsUp )// 时间到了可以执行程序了
                {
                    // 服务运行了
                    _serviceStatus = ServiceStatus.Running;

                    // 设置计时器,在无穷时间后再启用(实际上就是永远不启动计时器了--停止计时器计时)
                    SysTimer.Change( Timeout.Infinite, this.Config.ChkInterval );

                    //开始处理服务
                    this.StartService();
                }
            }
            catch ( Exception ex ) { this.ServiceException( ex ); }    // 处理服务执行过程中出现的异常
            finally
            {
                // 如果计时器不为空,则重新设置休眠的时间
                if ( SysTimer != null )
                {
                    if ( this.Config.TimerMode == TimerMode.Interval )// 定时轮询设置
                    {
                        // 重新启用计时器
                        SysTimer.Change( this.Config.ChkInterval, this.Config.ChkInterval );
                    }
                    else// 定时设置
                    {
                        // 用cft类计算下一次到期的时间
                        TimeSpan Interval = _timerControl.GetNextTimeUp();
                        // 重新启用计时器
                        SysTimer.Change( Interval, Interval );
                    }
                }
                _serviceStatus = ServiceStatus.Sleep;
            }
        }
        /// <summary>
        /// 开始服务
        /// </summary>
        protected abstract void StartService();
        /// <summary>
        /// 定时器初始化
        /// </summary>
        /// <param name="TimerMode"></param>
        /// <param name="StartTime"></param>
        /// <param name="ChkInterval"></param>
        protected abstract ServiceTimeConfig GetTimerConfig();
        /// <summary>
        /// 系统服务错误
        /// </summary>
        /// <param name="ex"></param>
        protected virtual void ServiceException( Exception ex ) { return; }
    }

    #region 定时器相关实体类
    /// <summary>
    /// 服务定时器配置
    /// </summary>
    public class ServiceTimeConfig
    {
        /// <summary>
        /// 轮询目录时间间隔(单位:毫秒)
        /// </summary>
        public int ChkInterval { get; set; }
        /// <summary>
        /// StartTime值为:
        ///
        /// TimerMode=TimerMode.Month    "09|04:30"    -表示每月9号的凌晨4点30分
        ///    TimerMode=TimerMode.Week     "0|04:30"    -表示每星期天的4点30分
        /// TimerMode=TimerMode.Week     "6|04:00"    -表示每星期6的4点30分
        ///    TimerMode=TimerMode.Day          "|04:00"    -表示每天的4点00分
        ///    TimerMode=TimerMode.Date "08-10|04:00"    -表示每年8月10号的4点00分执行一次
        ///    TimerMode=TimerMode.Year   "246|04"        -表示每年第246天的4点00分执行一次(可以不填写分钟默认为00)
        /// </summary>
        public string StartTime { get; set; }
        /// <summary>
        /// 服务器定时处理模型
        /// </summary>
        public TimerMode TimerMode { get; set; }
    }
    /// <summary>
    /// 服务处理方法
    /// </summary>
    public enum TimerMode
    {
        /// <summary>
        /// 轮询方式
        /// </summary>
        Interval = 0,
        /// <summary>
        /// 一个月中某个天数的指定时间
        /// </summary>
        Month = 1,
        /// <summary>
        /// 一周中的周几的指定时间
        /// </summary>
        Week = 2,
        /// <summary>
        /// 一天中的指定时间
        /// </summary>
        Day = 3,
        /// <summary>
        /// 一年中第几天的指定时间
        /// </summary>
        Year = 4,
        /// <summary>
        /// 一年中的指定日期的指定时间
        /// </summary>
        Date = 5,
        /// <summary>
        /// 未设置
        /// </summary>
        NoSet
    }
    /// <summary>
    /// 服务处理方法
    /// </summary>
    public enum ServiceStatus
    {
        /// <summary>
        /// 休眠中
        /// </summary>
        Sleep = 0,
        /// <summary>
        /// 服务在执行过程中
        /// </summary>
        Running = 1
    }
    #endregion

    #region 定时服务休眠时间计算类
    /// <summary>
    /// 定时服务休眠时间计算类
    /// </summary>
    public class TimerControl
    {
        #region 私有成员
        /// <summary>
        /// 间隔单位
        /// </summary>
        private TimerMode type;
        /// <summary>
        /// 月份
        /// </summary>
        private int Month;
        /// <summary>
        /// 天
        /// </summary>
        private int Day;
        /// <summary>
        /// 小时
        /// </summary>
        private int Hour;
        /// <summary>
        /// 分钟
        /// </summary>
        private int Minute = 0;
        #endregion

        #region 公共成员方法
        /// <summary>
        /// StartTime值为:
        ///
        /// TimerMode=TimerMode.Month    "09|04:30"    -表示每月9号的凌晨4点30分
        ///    TimerMode=TimerMode.Week     "0|04:30"    -表示每星期天的4点30分
        /// TimerMode=TimerMode.Week     "6|04:00"    -表示每星期6的4点30分
        ///    TimerMode=TimerMode.Day          "|04:00"    -表示每天的4点00分
        ///    TimerMode=TimerMode.Date "08-10|04:00"    -表示每年8月10号的4点00分执行一次
        ///    TimerMode=TimerMode.Year   "246|04"        -表示每年第246天的4点00分执行一次(可以不填写分钟默认为00)
        /// </summary>
        /// <param name="StartTime"></param>
        /// <param name="timeMode"></param>
        public TimerControl( string StartTime, TimerMode timeMode )
        {
            //Regex regEx = new Regex( @"(?<Type>[MWDY])(?<Days>/d+)?/|(?<Hour>/d+):?(?<Minute>[0-5]/d?)?", RegexOptions.Compiled | RegexOptions.IgnoreCase );
            Regex regEx = new Regex( @"^(?:(?<Month>[0]?/d|1[0-2])-)?(?<Days>/d{1,3})?/|(?<Hour>[01]?/d|2[0-3])(?::(?<Minute>[0-5]/d?))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase );

            this.type = timeMode;

            Match m = regEx.Match( StartTime );
            if ( m.Success )
            {
                if ( String.IsNullOrEmpty( m.Groups["Month"].Value ) && this.type == TimerMode.Date )
                    throw new Exception( "定时器时间配置异常!" );

                if ( !String.IsNullOrEmpty( m.Groups["Month"].Value ) )
                    this.Month = Convert.ToInt32( m.Groups["Month"].Value );

                if ( !String.IsNullOrEmpty( m.Groups["Days"].Value ) )
                    this.Day = Convert.ToInt32( m.Groups["Days"].Value );

                this.Hour = Convert.ToInt32( m.Groups["Hour"].Value );

                if ( !String.IsNullOrEmpty( m.Groups["Minute"].Value ) )
                    this.Minute = Convert.ToInt32( m.Groups["Minute"].Value );
            }
            else
                throw new Exception( "定时器时间配置异常!" );
        }
        /// <summary>
        /// 判断时间是否到了
        /// </summary>
        /// <returns></returns>
        public bool TimeIsUp
        {
            get
            {
                DateTime dt = DateTime.Now;
                switch ( type )
                {
                    case TimerMode.Day:
                        return ( dt.Hour == this.Hour && dt.Minute == this.Minute );
                    case TimerMode.Month:
                        return ( dt.Day == this.Day && dt.Hour == this.Hour && dt.Minute == this.Minute );
                    case TimerMode.Week:
                        return ( ( ( int )dt.DayOfWeek ) == this.Day && dt.Hour == this.Hour && dt.Minute == this.Minute );
                    case TimerMode.Date:
                        return ( dt.Month == this.Month && dt.Day == this.Day && dt.Hour == this.Hour && dt.Minute == this.Minute );
                    case TimerMode.Year:
                        return ( dt.DayOfYear == this.Day && dt.Hour == this.Hour && dt.Minute == this.Minute );
                }
                return false;
            }
        }
        /// <summary>
        /// 从现在起到下次时间到还有多少时间
        /// </summary>
        /// <returns></returns>
        public TimeSpan GetNextTimeUp()
        {
            ///目标时间
            DateTime _NextDateTime = this.GetNextDateTime();    // 保存下一次要执行的时间
            TimeSpan NextCrtFileTime = _NextDateTime - DateTime.Now;

            if ( ( int )NextCrtFileTime.TotalMilliseconds > int.MaxValue )    // 如果要休眠的时间间隔超过int类型可以表示的毫秒时,先休眠到int.MaxValue,然后再次休眠
                NextCrtFileTime = new TimeSpan( int.MaxValue );

            return NextCrtFileTime;
        }
        /// <summary>
        /// 获取下一次指定配置的时间是多少
        /// </summary>
        /// <returns></returns>
        public DateTime GetNextDateTime()
        {
            DateTime dt = DateTime.Now;
            DateTime now, target;
            switch ( this.type )
            {
                case TimerMode.Day:
                    #region 每天指定某时执行一次
                    now = new DateTime( 1, 1, 1, dt.Hour, dt.Minute, 0 );
                    target = new DateTime( 1, 1, 1, this.Hour, this.Minute, 0 );
                    if ( now.Ticks >= target.Ticks ) dt = dt.AddDays( 1.0 );    //如果当前时间小于指定时刻,则不需要加天

                    dt = new DateTime( dt.Year, dt.Month, dt.Day, this.Hour, this.Minute, 0 );
                    #endregion
                    break;
                case TimerMode.Month:
                    #region 每月指定某天某时执行一次
                    now = new DateTime( 1, 1, dt.Day, dt.Hour, dt.Minute, 0 );
                    target = new DateTime( 1, 1, this.Day, this.Hour, this.Minute, 0 );
                    if ( now.Ticks >= target.Ticks ) dt = dt.AddMonths( 1 );

                    dt = new DateTime( dt.Year, dt.Month, this.Day, this.Hour, this.Minute, 0 );
                    #endregion
                    break;
                case TimerMode.Week:
                    #region 每星期指定星期某时执行一次
                    int dow = ( int )dt.DayOfWeek;
                    now = new DateTime( 1, 1, dow + 1, dt.Hour, dt.Minute, 0 );
                    target = new DateTime( 1, 1, this.Day + 1, this.Hour, this.Minute, 0 );

                    if ( now.Ticks >= target.Ticks )
                        dt = dt.AddDays( this.Day - dow + 7 );
                    else
                        dt = dt.AddDays( this.Day - dow );

                    dt = new DateTime( dt.Year, dt.Month, dt.Day, this.Hour, this.Minute, 0 );
                    #endregion
                    break;
                case TimerMode.Date:
                    #region 每年指定某月某日某时执行一次
                    now = new DateTime( 1, dt.Month, dt.Day, dt.Hour, dt.Minute, 0 );
                    target = new DateTime( 1, this.Month, this.Day, this.Hour, this.Minute, 0 );
                    if ( now.Ticks >= target.Ticks ) dt = dt.AddYears( 1 );

                    dt = new DateTime( dt.Year, this.Month, this.Day, this.Hour, this.Minute, 0 );
                    #endregion
                    break;
                case TimerMode.Year:
                    #region 每年指定第N天某时执行一次
                    now = new DateTime( 1, 1, 1, dt.Hour, dt.Minute, 0 );
                    target = new DateTime( 1, 1, 1, this.Hour, this.Minute, 0 );
                    if ( dt.DayOfYear > this.Day || dt.DayOfYear == this.Day && now.Ticks >= target.Ticks ) dt = dt.AddYears( 1 );
                    dt = dt.AddDays( this.Day - dt.DayOfYear );

                    dt = new DateTime( dt.Year, dt.Month, dt.Day, this.Hour, this.Minute, 0 );
                    #endregion
                    break;
                default:
                    throw new Exception( "定时器时间配置异常!" );
            }

            return dt;
        }
        #endregion
    }
    #endregion
}
时间: 2024-10-13 13:18:00

C#开发系统服务时用的定时器组件的相关文章

【Android开发精要笔记】Android组件模型解析

Android组件模型解析 Android中的Mashup 将应用切分成不同类别的组件,通过统一的定位模型和接口标准将他们整合在一起,来共同完成某项任务.在Android的Mashup模式下,每个组件的功能都可以被充分的复用.来自不同应用的组件可以有机地结合在一起,共同完成任务. 基于Mashup的Android应用模型 三个基本要素:组件.连接.配置 接口就是实现单元.从代码来看,组件就是派生自特定接口或基类的子类的实现,如界面组件Activity就是指派生自android.app.Activ

QT开发(七)——QT按钮组件

QT开发(七)--QT按钮组件 QT中有六种按钮组件,分别是按压按钮QPushButton.工具按钮QToolButton.单选按钮QRadioButton.多选按钮QCheckBox.命令链接按钮QCommandLinkButton.按钮盒QButtonBox. 一.QPushButton组件 1.QPushButton组件简介 QPushButton组件用于接受用户点击事件,能够显示提示字符串,是功能性组件,需要父组件作为容器,能够在父组件中进行定位,用于执行命令或触发事件. QPushBu

嵌入式Linux裸机开发(九)——S5PV210定时器

嵌入式Linux裸机开发(九)--S5PV210定时器 S5PV210内部一共有四类定时器. 一.PWM定时器 1.PWM定时简介 S5PV210内部共有5个32bit的PWM定时器.PWM定时器可以生成内部中断.PWM定时器0.1.2.3具有PWM功能,可以驱动外部I/O信号.PWM定时器4是一个无外部引脚的内部定时器.PWM 定时器使用 PCLK_PSYS 作为时钟源. 每个定时器有一个由定时器时钟驱动的32位递减计数器.递减计数器的初始值是由TCNTBn自动装载而获得的.如果递减计数器减到

QT开发(六)——QT容器组件

QT开发(六)--QT容器组件 QT中有九种容器组件,分别是组合框QGroupBox.滚动区QScrollArea.工具箱QToolBox.选项卡QTabWidget.控件栈QWidgetStack.框架QFrame.组件QWidget.MDI窗口显示区QMdiArea.停靠窗口QDockWidget. 一.QGroupBox分组框 1.QGroupBox组件简介 QGroupBox为构建分组框提供了支持.分组框通常带有一个边框和一个标题栏,作为容器部件来使用,在其中可以布置各种窗口部件.分组框

前端开发:setTimeout与setInterval 定时器与异步循环数组

前端开发:setTimeout与setInterval 定时器与异步循环数组 前言: 开通博客园三个月以来,随笔记录了工作中遇到的大大小小的难题,也看过无数篇令人启发的文章,我觉得这样的环境是极好的,在与博友的分享中可以学到新的知识.得到先驱者的指正.解决工作中遇到的难题.近一个月工作繁忙,新的文章也迟迟未写,今天呢,过来深入了解一下 关于javascript定时器的知识: setTimeout与setInterval简述 setTimeout与setInterval使用方法基本相同,他们接受两

Angular开发实践(四):组件之间的交互

在Angular应用开发中,组件可以说是随处可见的.本篇文章将介绍几种常见的组件通讯场景,也就是让两个或多个组件之间交互的方法. 根据数据的传递方向,分为父组件向子组件传递.子组件向父组件传递及通过服务传递三种交互方法. 父组件向子组件传递 子组件通过@Input装饰器定义输入属性,然后父组件在引用子组件的时候通过这些输入属性向子组件传递数据,子组件可通过setter或ngOnChanges()来截听输入属性值的变化. 先定义两个组件,分别为子组件DemoChildComponent和父组件De

QT开发(九)——QT单元组件

QT开发(九)--QT单元组件 QT有三种单元组件,分别为列表单元组件QListWidget.树形单元组件QTreeWidget.表格单元组件QTableWidget. 一.QListWidget列表单元 1.QListWidget组件简介 QListWidget列表单元组件继承自QListView,是基于单元的列表组件.QListWidget可以显示一个清单,清单中的每个项目是QListWidgetItem的一个实例,每个项目可以通过QListWidgetItem来操作.可以通过QListWi

JS-自制提速小工具:开发页面时需要按比例计算宽高值的快速计算器

<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <meta name="author" content"郭菊锋,[email protected]"> <title>开发页面时需要按比例计算宽高值得快速计算器</title> </head> <body> <p id="

设置MyEclipse开发项目时使用的JDK

安装好MyEclipse之后,在MyEclipse中开发项目时,默认使用的是MyEclipse是自带的JDK,如下图所示: 如果我们需要使用自己安装好的JDK,那么就需要在MyEclipse中重新设置,设置步骤如下: Window→Preferences 弹出[Preferences]对话框,如下图所示: 点击[Add…]按钮,弹出[Add JRE]对话框,如下图所示 点击[Next>]按钮,进入下一个步骤,如下图所示: 选择要添加的JDK所在目录 点击[Finish]按钮后,就可以看到我们新添