天天看點

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.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;  

namespace BlueVision.SaYuan.WinService  

{  

    /// <summary>  

    /// 服務定時器管理  

    /// </summary>  

    public abstract class ServiceTimerControl  

    {  

        #region 私有成員  

        /// <summary>  

        /// 定時器  

        /// </summary>  

        private Timer SysTimer { get; set; }  

        /// 是否啟用定時器  

        private bool _EnabledTimer = true;  

        /// 服務執行狀态  

        private ServiceStatus _serviceStatus = ServiceStatus.Sleep;  

        /// 時間計算類  

        private TimerControl _timerControl = null;  

        /// 定時器配置  

        ServiceTimeConfig Config;  

        #endregion  

        #region 公共屬性  

        /// 擷取服務狀态  

        public ServiceStatus ServiceStatus { get { return _serviceStatus; } }  

        /// 計時模式  

        public TimerMode TimerMode  

        {  

            get  

            {  

                if ( Config == null ) Config = this.GetTimerConfig();  

                return Config.TimerMode;  

            }  

        }  

        /// 計時配置  

        public string StartTime { get { return ( Config == null ) ? "" : Config.StartTime; } }  

        public TimerControl TimerControl  

                if ( Config == null )  

                    Config = this.GetTimerConfig();  

                if ( _timerControl == null )  

                    _timerControl = new TimerControl( this.Config.StartTime, this.Config.TimerMode );  

                return _timerControl;  

        /// 停止  

        public void Stop()  

            _EnabledTimer = false;  

            SysTimer.Change( Timeout.Infinite, Timeout.Infinite );  

        /// 開始服務  

        public void Start()  

            _EnabledTimer = true;  

            Config = this.GetTimerConfig();  

            SysTimer = new Timer( new TimerCallback( this.TimerProcess ), AppDomain.CurrentDomain, 0, this.Config.ChkInterval );  

        /// 處理間隔服務  

        /// <param name="sender"></param>  

        private void TimerProcess( object sender )  

            if ( !_EnabledTimer ) return;  

            bool TimeIsUp = true;  

            if ( this.Config.TimerMode != TimerMode.Interval )  

                // 如果定時方式不是定時輪詢的話,就構造TimerControl類,該類用來計算每次執行完程式後  

                // 到下次執行服務時需要休眠的時間  

                try  

                {  

                    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;  

        protected abstract void StartService();  

        /// 定時器初始化  

        /// <param name="TimerMode"></param>  

        /// <param name="StartTime"></param>  

        /// <param name="ChkInterval"></param>  

        protected abstract ServiceTimeConfig GetTimerConfig();  

        /// 系統服務錯誤  

        /// <param name="ex"></param>  

        protected virtual void ServiceException( Exception ex ) { return; }  

    }  

    #region 定時器相關實體類  

    /// 服務定時器配置  

    public class ServiceTimeConfig  

        /// 輪詢目錄時間間隔(機關:毫秒)  

        public int ChkInterval { get; set; }  

        /// 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)  

        public string StartTime { get; set; }  

        /// 伺服器定時處理模型  

        public TimerMode TimerMode { get; set; }  

    /// 服務處理方法  

    public enum TimerMode  

        /// 輪詢方式  

        Interval = 0,  

        /// 一個月中某個天數的指定時間  

        Month = 1,  

        /// 一周中的周幾的指定時間  

        Week = 2,  

        /// 一天中的指定時間  

        Day = 3,  

        /// 一年中第幾天的指定時間  

        Year = 4,  

        /// 一年中的指定日期的指定時間  

        Date = 5,  

        /// 未設定  

        NoSet  

    public enum ServiceStatus  

        /// 休眠中  

        Sleep = 0,  

        /// 服務在執行過程中  

        Running = 1  

    #endregion  

    #region 定時服務休眠時間計算類  

    /// 定時服務休眠時間計算類  

    public class TimerControl  

        /// 間隔機關  

        private TimerMode type;  

        /// 月份  

        private int Month;  

        /// 天  

        private int Day;  

        /// 小時  

        private int Hour;  

        /// 分鐘  

        private int Minute = 0;  

        #region 公共成員方法  

        /// <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( "定時器時間配置異常!" );  

        /// 判斷時間是否到了  

        /// <returns></returns>  

        public bool TimeIsUp  

                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;  

        /// 從現在起到下次時間到還有多少時間  

        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;  

        /// 擷取下一次指定配置的時間是多少  

        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 );  

                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 );  

                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 );  

                case TimerMode.Year:  

                    #region 每年指定第N天某時執行一次  

                    if ( dt.DayOfYear > this.Day || dt.DayOfYear == this.Day && now.Ticks >= target.Ticks ) dt = dt.AddYears( 1 );  

                    dt = dt.AddDays( this.Day - dt.DayOfYear );  

                default:  

            return dt;