假设我的订单处理系统有这样的需求:将一天 24 小时分为 4 个时段,凌晨 2:15 到 8:30 采用 A 类型的处理器处理接收到的订单, 8:30 到 14:00 采用 B 类型的处理器, 14:00 到 20:00 采用 C 类型的处理器, 20:00 到第二天凌晨 2:15 采用 D 类型的处理器。
即我们的订单处理器需要在任一天的 2:15 、 8:30 、 14:00 、 20:00 这四个时刻发生切换,这就是一个循环切换器所要做的工作。
我设计了 ESBasic.Threading.Application. ICircleTaskSwitcher (循环任务切换器)来对上述例子所要求的切换器进行抽象。 ICircleTaskSwitcher 会在需要切换的时间点触发事件,通知预订者要切换到哪个处理器(或任务)。
循环任务切换器的形象示意图如下:2. 适用场合:
如果满足以下条件,则可以使用 ICircleTaskSwitcher :
(1) 循环的周期是一天。
(2) 将一天切分为 N 个( N>=2 )连续的时间段。
(3) 需要在每个时间段的起始进行切换任务。
3 .设计思想与实现
ICircleTaskSwitcher 的设计思路是这样的:
(1) 我们可以使用前面介绍的 ShortTime 来记录每个发生任务切换的时间点。
(2) 使用前面介绍的 Circle 来将所有切换的时间点组成一个“圈”,这样便可循环地取到下一个切换时刻。
(3) 我们可以使用循环引擎 AgileCycleEngine 来对是否到达某个切换时间点进行检测。
(4) 使用泛型参数来抽象切换的任务的类型。
(5) 使用事件通知预订者切换时刻的到来。
ICircleTaskSwitcher 的接口定义如下:
{
/// <summary>
/// TaskDictionarykey为任务的起始点(hour),value为对应的任务。
/// </summary>
IDictionary < ShortTime ,TTask > TaskDictionary{ get ; set ;}
TTaskCurrentTask{ get ;}
void Initialize();
/// <summary>
/// TaskSwitched当任务发生切换时,触发此事件,事件参数为刚得到控制权的任务。
/// </summary>
event CbGeneric < TTask > TaskSwitched;
}
TaskDictionary 属性的键值即是每个切换的时间点,字典的值表示从切换时刻开始要进行的具体任务。
当切换时刻来到时, TaskSwitched 事件将被触发,事件的参数表示从该时刻起要执行的任务对象。
关于 CircleTaskSwitcher 的实现要注意以下几点:
(1) Initialize 方法主要做了三件事情:一是将 N 个切换时间点按照从小到大的顺序进行排序(因为 TaskDictionary 的键不一定是有序的),然后赋值给 Circle ,这样才能做有序的循环;二是找到当前时间所位于的时间段(因为我们可能在任何时刻启动切换器),并设置正确的当前任务;最后就初始化并启动循环引擎。
(2) 在循环引擎的检测方法 EngineAction 中,我们首先判断是否到达了下一个切换时间点,如果是,则触发切换事件。
从 CircleTaskSwitcher 的源码我们看到,在有了我们前面章节介绍的 Circle 、 ShortTime 、 AgileCycleEngine 等可复用类的帮助下, CircleTaskSwitcher 的实现真是相当的简洁和简单,并且每个类的概念也非常的清晰。这也就是可复用类所带来的好处的一个证明吧。
4. 使用时的注意事项
(1) 由于循环引擎的检测时间间隔是 1 秒――这也是循环引擎能设置的最小检测时间间隔,所以,切换时刻不是非常精确,最多时会有 1 秒的误差。
(2)
关于
TTask
泛型参数,在具体应用中,不一定非得是某个像订单处理器那样的引用类型。比如,我们希望在不同的时间段,给出的商品折扣不一样,那么,
TTask
完全可以是
float
,用来记录每个时间段具体的折扣值。
5. 扩展
循环任务切换器
ICircleTaskSwitcher
暂时没有任何扩展。
注:ESBasic源码可到 http://esbasic.codeplex.com/ 下载。
ESBasic讨论:37677395
ESBasic开源前言