自定义定时组件

  工作了这么久,封装过一部分Helper,也写过一些控件,但也没写过属于自己的框架,这次写的这个我觉得是一个组件而已,是一个定时组件。是一个定时器组件,有别于.NET Framework里面提供的几个Timer。首先说说背景,就发现现在手头上的工作离不开定时,定时做一个任务,什么都是定时去做什么什么,隔某段时间干某件事情,都离不开“定时”这个词。众所周知这个要用到多线程,在多篇关于多线程的文章里面有提过做一些周期性的操作时最好用Timer,当然这个Timer肯定是Threading.Timer,并不是WinForm控件里面的Timer。但我觉得在我的应用中Timer不够满足需求。

1.Timer只能在做任务与任务之间相隔一段时间的操作,如下图

但我需要的是这次任务开始的时刻到下次任务开始时刻是相隔同等的时间,又如下图

这样的情况下Timer则不能满足需求。

2.Timer的时间间隔一般是定的,但是如果要每次执行完任务要变动一下休眠的时间, 则需要调用Timer的Change方法。

3.Timer的休眠时间不是通过整形确定休眠的毫秒数,就是用一个TimeSpan来确定,对于那种到每天多少多少时刻或者到每小时的多少分执行一次的任务来说也不能够完全方便使用

对于上面这三种,鄙人对定时器封装了一下,弃用了Timer,还是用回了原有的Thread,定义了一种描述重复时间的模式字符串,计算出TimeSpan,从而调用Thread的Sleep()方法来休眠。下面展示整个组件的类图

最底下是两个关于时间计算方面的类,两个代理是两种任务方法的委托,基类BaseCycleMission是周期任务的积累,实现了ICycle接口,主要进行对任务线程的操控(开始,停止等),继承他的两个子类一个是实现上文第一点中我后来描述那种需求,一个类似于原有Timer的功能。它们各自使用不同的委托。MissionManager只是对所有周期任务的一个管理,统一去开启或暂停某一类的任务。

时间计算模块

  那首先来介绍一下定义的字符串模式。现在遇到的周期是有两种模式,

  • 一种是每隔多长时间要执行一次的任务,这个是最普通的周期形式,以每个5分钟为例,完整的形式是“-99--99--99 -99:05:00”,“-99”是缺省的意思,当然还有其他简写的模式;
  • 另一种是没到什么什么时候执行一次任务,例如没到中午12点为例完整的形式是“ff-ff-ff 12: ff:ff”,“ff”是默认的形式,当然也可以用“FF”,这里也有其他简写的模式。

所有字符串的模式如下表所示


 


每到***时刻


每隔***时间


完整


ffff-ff-ff ff:ff:ff 或
ff-ff-ff ff:ff:ff


-99--99--99 -99:-99:-99


日期部分


ffff-ff-ff 或
ff-ff-ff


-99--99--99


时间部分


ff:ff:ff 或
ff:ff:ff


-99:-99:-99


时间简写


ff:ff 或
ff:ff


-99:-99

  那么时间计算模块的处理流程是,给定了相应的模式字符串,TimePointConverter借助正则表达式匹配出对应的模式,返回匹配出来的年月日时分秒各个值,得出结果之后就调用SleepTimeProvider来计算出线程所要休眠的时间。下面则展示一下两个类的部分代码

 1     public class TimePointConverter
 2     {
 3         //其他成员
 4         private int[] DateTimeFixBuilder(string timeStr)
 5         {
 6             int[] result = null;
 7
 8             string[] dtArray = timeStr.Split();
 9             string[] dateArray = dtArray[0].Split(‘-‘);
10             string[] timeArray = dtArray[1].Split(‘:‘);
11
12             uint year,month,date;
13             uint hour, minute, second;
14             uint.TryParse(dateArray[0], out year);
15             uint.TryParse(dateArray[1], out month);
16             uint.TryParse(dateArray[2], out date);
17
18             uint.TryParse(timeArray[0], out hour);
19             uint.TryParse(timeArray[1], out minute);
20             uint.TryParse(timeArray[2], out second);
21
22             //return InnerFixBuilder(year, month, date, hour, minute, second);
23             result = new int[] { (int)year, (int)month, (int)date, (int)hour, (int)minute, (int)second };
24             return result;
25         }
26         //其他成员
27     }
 1     public class SleepTimeProvider
 2 {
 3         //其他成员
 4         public TimeSpan InnerFixBuilder(uint year, uint month, uint date, uint hour, uint minute, uint second)
 5                 {
 6             uint[] uintTimeArray = new uint[6] { year, month, date, hour, minute, second };
 7             int[] intNowArray = new int[6]
 8             {
 9                 DateTime.Now.Year,DateTime.Now.Month,DateTime.Now.Day,
10                 DateTime.Now.Hour,DateTime.Now.Minute,DateTime.Now.Second
11             };
12             int[] intTimeArray = new int[6];
13
14             intTimeArray[0] = uintTimeArray[0] == 0 ? -DateTime.Now.Year : (int)uintTimeArray[0];
15             for (int i = 1; i < uintTimeArray.Length; i++)
16             {
17                 intTimeArray[i] = intTimeArray[i - 1] < 0 && uintTimeArray[i] == 0 ?
18                     -intNowArray[i] : (int)uintTimeArray[i];
19             }
20             DateTime goalTime = new DateTime(Math.Abs(intTimeArray[0]),
21                 Math.Abs(intTimeArray[1]),
22                 Math.Abs(intTimeArray[2]),
23                 Math.Abs(intTimeArray[3]),
24                 Math.Abs(intTimeArray[4]),
25                 Math.Abs(intTimeArray[5]));
26
27
28             if (goalTime < DateTime.Now)
29             {
30                 int max = -1;
31                 for (int i = intTimeArray.Length - 1; i >= 0; i--)
32                 {
33                     if (intTimeArray[i] < 0 && i > max)
34                     {
35                         max = i;
36                         intTimeArray[i]--;
37                     }
38                     intTimeArray[i] = Math.Abs(intTimeArray[i]);
39                 }
40                 goalTime = new DateTime(Math.Abs(intTimeArray[0]),
41                 Math.Abs(intTimeArray[1]),
42                 Math.Abs(intTimeArray[2]),
43                 Math.Abs(intTimeArray[3]),
44                 Math.Abs(intTimeArray[4]),
45                 Math.Abs(intTimeArray[5]));
46             }
47             return goalTime - DateTime.Now;
48         }
49
50         //其他成员
51 }

线程调用模块

  线程调用模块是任务执行的核心部分,MissionEntiy是对线程操作的封装,主要负责开始,停止,暂停等操作。Thread用的是后台线程,对线程操作时也多做几个判断。例如暂停那个操作的定义如下

 1         public bool Pause()
 2         {
 3             if (actionThread == null) return false;
 4             if (actionThread.ThreadState == (System.Threading.ThreadState.Running | ThreadState.Background) ||
 5                 actionThread.ThreadState == (System.Threading.ThreadState.WaitSleepJoin | ThreadState.Background))
 6             {
 7                 actionThread.Suspend();
 8                 return true;
 9             }
10             return false;
11         }

  CycleMission是真正的任务载体,里面都同样有对线程的操作,但是又外加了一些时间处理,最核心的是让线程的BuildMainAction方法,这个方法是计算出要休眠的时间,让线程休眠,到点时调用适当的方法委托。

 1     public class BaseCycleMission:ICycleMission
 2     {
 3         //其他成员
 4         protected void BuildMainAction(string normalCycle, string overTimeCycle, object overTimeDelegate, bool isSleepBefore,bool isInclude)
 5         {
 6             mainAction = () =>
 7             {
 8                 TimeSpan sleepTime=TimeSpan.MinValue;
 9                 bool result = true;
10                 TimePointConvert.CircleType type ;
11
12                 #region 提前休眠
13                 if (isSleepBefore)
14                 {
15                     type = TimePointConvert.Default.PraseType(normalCycle);
16
17                     if (type == TimePointConvert.CircleType.Interval)
18                         sleepTime = SleepTimeProvider.Defult.InnerIntervalBuilder(
19                             TimePointConvert.Default.ConvertCircle(normalCycle));
20                     else
21                         sleepTime = SleepTimeProvider.Defult.InnerFixBuilder(
22                         TimePointConvert.Default.ConvertCircle(normalCycle));
23                     if (sleepTime.TotalMilliseconds > 0)
24                         Thread.Sleep(sleepTime);
25                 }
26                 #endregion
27
28                 while (true)
29                 {
30                     #region 计算时间
31                     if (isInclude)
32                     {
33                         if (result)
34                         {
35                             type = TimePointConvert.Default.PraseType(normalCycle);
36
37
38
39                             type = TimePointConvert.Default.PraseType(overTimeCycle);
40
41                             sleepTime = type == TimePointConvert.CircleType.Interval ?
42                                 SleepTimeProvider.Defult.InnerIntervalBuilder(
43                                     TimePointConvert.Default.ConvertCircle(overTimeCycle)) :
44                                 SleepTimeProvider.Defult.InnerFixBuilder(
45                                     TimePointConvert.Default.ConvertCircle(overTimeCycle));
46
47
48                         }
49                     }
50                     #endregion
51
52                     #region 执行方法
53
54                     if(overTimeDelegate is OverTimeCycleDelegate)
55                         result = (overTimeDelegate as OverTimeCycleDelegate).Invoke();
56                     else
57                     {
58                         (overTimeDelegate as CycleDelegate).Invoke();
59                         result = true;
60                     }
61                     #endregion
62
63                     #region 计算时间
64                     if (!isInclude)
65                     {
66                         if (result)
67                         {
68
69                             type = TimePointConvert.Default.PraseType(normalCycle);
70
71
72                             sleepTime = type == TimePointConvert.CircleType.Interval ?
73                                 SleepTimeProvider.Defult.InnerIntervalBuilder(
74                                     TimePointConvert.Default.ConvertCircle(normalCycle)) :
75                                 SleepTimeProvider.Defult.InnerFixBuilder(
76                                     TimePointConvert.Default.ConvertCircle(normalCycle));
77                         }
78                         else
79                         {
80                             type = TimePointConvert.Default.PraseType(overTimeCycle);
81
82                             sleepTime = type == TimePointConvert.CircleType.Interval ?
83                                 SleepTimeProvider.Defult.InnerIntervalBuilder(
84                                     TimePointConvert.Default.ConvertCircle(overTimeCycle)) :
85                                 SleepTimeProvider.Defult.InnerFixBuilder(
86                                     TimePointConvert.Default.ConvertCircle(overTimeCycle));
87                         }
88                     }
89                     #endregion
90
91                     if (sleepTime.TotalMilliseconds > 0)
92                         Thread.Sleep(sleepTime);
93
94                 }
95             };
96         }
97         //其他成员
98 }

当然调用不是调用这个方法,调用只是调用它两个几类ExceptCycleMission和IncludeCycleMission,分别代表任务执行的时间不包括在周期里面和包括在周期里面两种。

管理器部分

  管理器主要是一个字典集,是一个ICycleMission和字符串的字典集,里面包含了对集合里面所有元素的操作:增加,删除,运行,恢复,暂停,停止。除了删除和增加,其他都包含了类似下面的方法

RunAllMission()
RunAllIncludeCycleMission()
RunAllExceptCycleMission()
RunMissionAmong(params string[] missionNames)
RunMissionExcept(params string[] missionNames)

但是这堆方法里面都调用了CallAction这个方法,

        private void CallAction(IEnumerable<ICycleMission> missionCollection,Action method)
        {
            if (missionCollection == null || method == null||missionCollection.Count()==0) return;

            foreach (ICycleMission item in missionCollection)
            {
                method.Method.Invoke(item, null);
            }
        }

例如在RunAllExceptCycleMission()方法里面调用如下

        public void RunAllExceptCycleMission()
        {
            CallAction(this.Values.Where(c => c is ExceptCycleMission), BaseCycleMission.Default.RunMission);
        }

  做这个组件应该是小题大做,毕竟看了那么久的《MVC框架揭秘》手痒,写了这个东西觉得框架里面的结构要设计好,命名也要命名好。

自定义定时组件

时间: 2024-10-10 02:50:11

自定义定时组件的相关文章

自定义SlidingMenu组件

在网上学习了自定义slidingmenu组件,这里记录下其中的关键点. SlidingMenu其实是一个HorizontalScrollView,里面有两个布局,通过重写几个方法达到侧滑的效果. 首先原理是在LinearLayout外嵌套了HorizontalScrollView,SlidingMenu继承HorizontalScrollView. 重写onMeasure(int widthMeasureSpec, int heightMeasureSpec),onLayout(boolean

Ember.js 入门指南——自定义包裹组件的HTML标签

按照惯例,先做好准备工作,使用Ember CLI命令生成演示所需的文件: ember g route customizing-component-element ember g component customizing-component-element ember g route home ember g route about 默认情况下,组件会被包裹在div标签内.比如,组件渲染之后得到下面的代码: <div id="ember180">   <h1>M

java实现自定义同步组件的过程

实现同步组件twinsLock:可以允许两个线程同时获取到锁,多出的其它线程将被阻塞. 以下是自定义的同步组件类,一般我们将自定义同步器Sync定义为同步组件TwinsLock的静态内部类. 实现同步器需要继承AbstractQueuedSynchronizer并覆盖相应的方法. package com.lock; import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.AbstractQueuedSynchr

Flex 自定义 Zlert 组件!

说明: 原生的 Alert.show 参数,要显示的按钮你只能 Alert.OK | Alert.Cancel 这样; 自定义 Zlert 参数跟原生的 差不多,按钮写法是这样写的: {"btnId":"确定", "btnId2":"其他按钮名称"} ZlertWindow.mxml: com/components <?xml version="1.0" encoding="utf-8&qu

自定义JS组件——系列1(TableGrid | Toolbar | LinkButton | Pager)

2月份第一次JS组件,写了几个:TableGrid, Toolbar, LinkButton, Pager,现在发出来. TableGrid可以包含Toolbar, Pager. Toolbar可以包含LinkButton.这样就构成了具有工具栏.分页栏的数据表格控件.也就是说,这4个组件可以独立使用,也可以结合使用.通篇只采用一种结构来编写,若能看懂编写规则,往后就可以按照这种模式自定义JS控件了. 1 var fjn=fjn?fjn:{}; 2 (function($,global){ 3

Android Studio开发基础之自定义View组件

一般情况下,不直接使用View和ViewGroup类,而是使用使用其子类.例如要显示一张图片可以用View类的子类ImageView,开发自定义View组件可分为两个主要步骤: 一.创建一个继承自android.view.View类的View类,并且重写构造方法. 如下,新建一个名为MyView.Java的Java类文件,重写一个带Context的构造方法和onDraw()方法(用来重新绘制Activity窗口的背景). package com.example.lhb.contentprovid

自定义Android组件之组合方式创建密码框组件

Android中所有控件(也称组件)都继承自adnroid.view.View类,android.view.ViewGroup是View类的重要子类,绝大多书的布局类就继承自ViewGroup类. 附上一张基于Android Api21的View和Widget类图 自定义Android组件基本可以从2个入口着手,一是继承Viewe类拿起画笔和画布绘制组件,而是通过继承View的子类和组合已有的组件的方式构造自定义的组件. 本文通过自定义一个PassWordView组件来实现密码能够通过点击点选框

#003 React 组件 继承 自定义的组件

主题:React组件 继承 自定义的 组件 一.需求说明 情况说明: 有A,B,C,D 四个组件,里面都有一些公用的逻辑,比如 设置数据,获取数据,有某些公用的的属性,不想在 每一个 组件里面写这些属性,怎么办? [和 面向对象的语言,C#,Java 的基类 思想是 一样的] 如果公用的东西,是一些方法,可以 使用 React 的 Mixins(ES5) ,高阶组件(ES6)[高阶函数不太了解,如何使用,去找下资料 ] 但是如果有公用的属性,那么就有点 力不从心了 在想,React 中,是否可用

【Android 应用开发】 自定义 View 组件 -- 圆形进度条

转载著名出处 : http://blog.csdn.net/shulianghan/article/details/40351487 代码下载 : -- CSDN 下载地址 : http://download.csdn.net/detail/han1202012/8069497 ; -- GitHub 地址 : https://github.com/han1202012/CircleProcess.git ; -- 工程示例 : 一. 相关知识点解析 1. 自定义 View 组件构造方法 构造方