优先级管理器 IPriorityManager -- ESBasic 可复用的.NET类库(14)

1.缘起:

    假设我们的订单处理系统所要处理的订单是有优先级的,也就是说,不同的订单类型所要求被处理的紧迫程度不同,对那些优先级高的注单要先处理,对于优先级低的注单可稍后处理。对于处于同一优先级的订单了,就按照其到达的先后顺序进行处理。

    这是一个典型的管理具有优先级的对象的需求,注单就是具有优先级(With Priority)的对象。我设计了ESBasic.ObjectManagement.Managers.IPriorityManager优先级管理器(确切地说,应该称之为“具有优先级对象的管理器”)来对类似的对象进行管理。

     优先级管理器的形象示意图如下:

 

2.适用场合:

    如果你的系统需要对被管理的对象进行优先级分级,并满足以下条件,则可使用IPriorityManager:

(1)对象需要按优先级别(PriorityLevel)进行分类。

(2)优先级别的划分是固定的,不会随系统的运行而发生变化。

(3)对处于同一优先级别的对象,采用先来后到的顺序进行“第二优先级”的高低确定。

(4)优先级别可以使用>=0的连续整数来表示。

 

3.设计思想与实现

    在前面的叙述中,具有优先级对象的管理器的功能职责是相当清晰明了的,在进入其实现之前,首先我们要解决的一个问题是,如何对处于同一优先级别的对象进行管理。根据前面的需求描述,如果两个对象处于相同的优先级别,则先到达的对象的优先程度(即所谓的“第二优先级”)更高。

    我使用ESBasic.ObjectManagement.Managers.ISamePriorityObjectManager(同一优先级别对象管理器)来管理属于同一优先级别的所有对象,其接口定义如下:

     public interface ISamePriorityObjectManager<T>
    {   
        /// <summary>
        /// AddWaiter 添加一个等待者。如果等待者在管理器中已经存在,则直接返回。
        /// </summary>       
        void AddWaiter(T waiter);

        /// <summary>
        /// Count 当前管理器中等待者的数量。
        /// </summary>
        int Count { get; }

        /// <summary>
        /// GetNextWaiter 返回等待时间最长的waiter。
        /// 注意,返回时并不会从等待列表中删除waiter。如果要删除某个等待者,请调用RemoveWaiter。
        /// </summary>       
        T GetNextWaiter();

        /// <summary>
        /// GetWaitersByPriority 按照等待者加入的先后顺序返回等待者数组,数组中index越小的等待者其等待时间越长,其优先级也越高。
        /// </summary>       
        T[] GetWaitersByPriority();

        /// <summary>
        /// RemoveWaiter 从管理器中移除指定的等待者。
        /// </summary>        
        void RemoveWaiter(T waiter);

        /// <summary>
        /// Clear 清空管理器中的所有等待者。
        /// </summary>
        void Clear();

        /// <summary>
        /// Contains 管理器中是否存在指定的等待者。
        /// </summary>       
        bool Contains(T waiter);
    }

在ISamePriorityObjectManager所表述的语义环境中,被管理的对象称为“等待者”waiter――这表示一个对象等待被处理。

关于SamePriorityObjectManager的实现,有以下几点需要说明:

(1)其内部是使用LinkedList而不是Queue来存储等待者的,其主要原因在于SamePriorityObjectManager需要支持移除管理器中任一等待者的RemoveWaiter方法。由于Queue本身不支持任意位置的删除功能,所以我使用了LinkedList。新加入的等待者将被放在LinkedList的最后位置。

(2)当管理器中没有任何等待者时,GetNextWaiter方法将返回default(T),如果T是值类型,则此时GetNextWaiter返回的可能并不是一个你所期望的对象。所以,如果T是值类型,在调用GetNextWaiter之前先访问一下其Count属性确保管理器中还有等待者存在。

(3)SamePriorityObjectManager使用了前面介绍的SmartRWLocker来对内部的waiterList进行读写锁控制。

 

在讨论完SamePriorityObjectManager的实现以后,我们将注意力转移到本节的主角IPriorityManager上来,IPriorityManager的接口定义如下:

     /// <summary>
    /// IPriorityManager 具有优先级的对象的管理器。
    /// </summary>
    /// <typeparam name="T">被管理的对象的类型,必须从IPriorityObject继承。</typeparam>
    public interface IPriorityManager<T> : ISamePriorityObjectManager<T> where T : class, IPriorityObject 
    {
        int PriorityLevelCount { get; set; }
    }

IPriorityManager接口直接从ISamePriorityObjectManager继承,并没有多加任何方法,唯一增加的就是一个PriorityLevelCount属性和要求被管理的对象的类型必须是从IPriorityObject接口继承的一个泛型约束。

PriorityLevelCount用于设定你的系统需要有几种优先级别。比如,我的订单基于紧急的优先级可分为紧急、普通、不紧急三种,那么就可将PriorityLevelCount属性设置为3。

一个类型从IPriorityObject接口继承,就表明它的实例是具有优先级属性的对象。

    /// <summary>
    /// IPriorityObject 具有优先级的对象的接口。
    /// </summary>
    public interface IPriorityObject
    {
        int PriorityLevel { get; }
    }

为什么ISamePriorityObjectManager没有要求被管理的对象继承自IPriorityObject接口了?这是因为在ISamePriorityObjectManager的职责中,其仅仅是根据对象的先后顺序来确定“第二优先级”的,这并不是真正意义上的优先级别,所以没有必要为其单独抽象出一个IPriorityObject接口来。同时,ISamePriorityObjectManager不要求被管理的对象继承自IPriorityObject接口也是为了扩大其被单独复用的范围。

       IPriorityManager接口直接从ISamePriorityObjectManager接口继承,说明IPriorityManager实际上要做工作与ISamePriorityObjectManager是相同的,只不过IPriorityManager管理的对象需要首先按优先级别进行分类,然后再使用ISamePriorityObjectManager管理处于同一优先级别的对象。

    接下来我们看PriorityManager的具体实现。

在PriorityManager中,有这样的一个约定:优先级别是用int表示的,其值是从0开始连续的一串整数,整数值越小,表明优先级越高。当Initialize方法被执行后,优先等级的范围就被固定下来。比如PriorityLevelCount值设为4,则PriorityManager所支持的优先等级即为:0,1,2,3。

基于这样的约定,PriorityManager内部使用了一个ISamePriorityObjectManager数组,数组的索引值就对应着优先级别值。比如,数组中index为1的ISamePriorityObjectManager管理器中的所有对象的优先级别值都是1。

有了这两点认识,再看PriorityManager的源码就相当容易了,下面是其中的关键点:

(1)在类似AddWaiter、RemoveWaiter这样的方法实现中,都是先通过其参数对象的PriorityLevel属性定位到对应的ISamePriorityObjectManager管理器,然后再做进一步的处理的。

(2)如果目标对象的PriorityLevel属性值超过了约定的范围,PriorityManager会根据当前的情况做灵活的处理。比如,如果是调用AddWaiter加入一个这样的对象,则会抛出一个“不支持该优先级别”的异常;而如果是在类似RemoveWaiter这样的方法中,则会忽略这个对象。

(3)如果PriorityManager管理器中没有任何对象时,PriorityManager的GetNextWaiter方法直接返回null,而不是default(T),这是因为在PriorityManager定义的泛型约束中,要求T必须是一个引用类型。这就没有了前面提到的SamePriorityObjectManager的GetNextWaiter方法的返回值可能导致的问题。

(4)GetWaitersByPriority方法返回的对象数组具有这样的特征:优先级别越高的对象,其在数组中的位置索引就越小;同一优先级别的对象,加入时间越早的,其在数组中的位置索引越小。

 

4. 使用时的注意事项

(1)     如果你的系统仅仅需要按照对象的到达顺序来决定先后处理的顺序,那么直接使用ISamePriorityObjectManager就可以满足需求了,没有必要使用IPriorityManager这个更复杂的类。使用ISamePriorityObjectManager还有一个好处就是,被管理的对象不需要实现IPriorityObject接口,这样使用起来会更加方便。

(2)     如果在你的系统中不是使用0,1,2,3…这样的数值来表示优先级别的,那么你可以建立一个转换映射来完成优先级别值到数字的转换。并遵从PriorityManager所要求的约定。

(3)     PriorityManager的Initialize方法一旦被调用后,其PriorityLevelCount属性便不应该被修改。或者说,即使该属性在之后被修改,也不会产生任何效果。

 

5.扩展

优先级管理器PriorityManager暂时没有任何扩展。

 

注: ESBasic已经开源,点击这里下载源码。
    ESBasic开源前言

 

 

时间: 2024-10-24 05:33:46

优先级管理器 IPriorityManager -- ESBasic 可复用的.NET类库(14)的相关文章

Round缓存管理器RoundCacheManager--ESBasic 可复用的.NET类库(26)

1.缘起:     在增量自动获取器章节的缘起部分,我们曾提到增量缓存,本节我们将深入探讨它以及用于管理增量缓存的管理器.我们还是以增量自动获取器章节提到的例子作为基础,并做更进一步的讨论.       OK,现在让我们开始这有趣的旅程. 首先,基于前面例子给出的上下文,我们知道IIncreaseAutoRetriever获取的增量是用于累积当天的已成交订单报表的."当天已成交报表"就是一个典型的增量缓存,每当有新的增量到来,都会累加到上面. 我们假设今天是2009.07.08,那么我

定时刷新缓存管理器 IRefreshableCacheManager --ESBasic 可复用的.NET类库(16)

1.缘起:     为了提升系统的性能或减轻数据库的压力等原因,我们经常在系统中使用缓存来把那些经常使用的数据保留在内存中.如果因为某些原因,缓存中这些经常使用的数据不能及时与数据源进行同步更新,那么采用定时刷新缓存中的数据有可能就是一种合适的选择.     如果你的缓存是定时刷新,那么你就需要自己为其维护一个定时器或循环引擎.如果你的系统中像这样定时刷新的缓存有多个,而且每个缓存定时刷新的时间间隔又要求不一样,那么,使这些缓存按照你预想的情况进行运转,你就需要花费一些气力.     我设计了定

对象管理器 IObjectManager -- ESBasic 可复用的.NET类库(12)

1.缘起: 我们经常需要对一些动态对象进行管理,最常见的例子就是在线用户管理.当一个用户成功登陆到服务器后,我们就需要将其管理起来:当他退出后,就不再需要再管理他了.这就是所谓动态对象的含义,这些对象并不是一直需要被管理,只有当其被激活后,才需要被管理.它们总是在"激活"状态和"非激活"状态之间不断地切换. 我设计了对象管理器ESBasic.ObjectManagement.Managers.IObjectManager来管理类似的动态对象.这个类是ESBasic提

分组对象管理器 IGroupingObjectManager--ESBasic 可复用的.NET类库(13)

1.缘起:     假设我们的订单系统需要管理所有未处理的订单,而客人经常需要查询属于自己的未处理的订单列表.另外,可能客服人员也需要根据订单ID迅速地找到对应的未处理订单.基于第一个需求,我们就可以将未处理的订单依据客人的帐号进行分组管理.     我设计了ESBasic.ObjectManagement.Managers.IGroupingObjectManager分组对象管理器来完成对对象进行分组管理的功能.       分组对象管理器的形象示意图如下:      2.适用场合: 当你的需

对象获取器IObjectRetriever -- ESBasic 可复用的.NET类库(17)

1.缘起: ESBasic中许多管理对象的容器都用到了这个ESBasic.ObjectManagement.IObjectRetriever接口,所以单独将其提出来介绍一下. 当我们向对象容器(Container)请求某个对象时,也许目标对象还未加载到容器中,这可能是因为容器在初始化的时候就没有加载这个对象,也有可能是因为这个对象是容器初始化以后新增到数据库(当然也有可能是其它的持久化存储)的.在这种情况下,对象容器就可以借助IObjectRetriever来将目标对象从数据库等持久化存储中加载

循环任务切换器 CircleTaskSwitcher -- ESBasic 可复用的.NET类库(06)

 1.缘起:     假设我的订单处理系统有这样的需求:将一天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. ICir

片段整合提取器 ScatteredSegmentPicker -- ESBasic 可复用的.NET类库(21)

1.缘起:     当数据源中的数据量多到一定程度时,我们在查询时就经常使用分页策略.如果数据源是一个完整的整体,这没有什么大不了的,我们经常就在做类似的事情.但是,如果数据源不是一个完整的整体,而是由很多有序的片段构成的,并且不同的片段可能位于不同的位置(比如,位于不同的服务器节点上的内存中),甚至,每个片段内的数据还会随着时间的变化而变化的.     在这种假设的情况下,来从这个"虚拟的完整"数据源获取某个分页就不再是那么简单的事情了.一个分页可能位于一个片段的内部的某个区间,也有

ESBasic 可复用的.NET类库(00) -- 开源前言(附下载)

自从03年正式使用.NET开发以来,已经走过了6个年头,这期间我积累了几套类库和框架,ESBasic便是其中最基础的一个类库.ESBasic是Enterprise Service Basic的缩写,虽然也简写为ESB,但是它和Enterprise Service Bus(企业服务总线)没有任何关系.ESBasic是我能够快速和高效开发应用程序的利器之一,开这个专门的blog是想将它介绍给大家,希望能对大家有所启发. ESBasic覆盖的内容包括:对象管理.插件.网络(Socket).多线程.Em

回调定时器ICallbackTimer --ESBasic 可复用的.NET类库(07)

 1.缘起:     举个例子也许就能够说清楚回调定时器的用途.假设我的订单系统接收各种不同类型的订单,当订单A进来时,系统根据订单的类型和其它特征进行综合判断后,决定A订单要在2秒之后被方法M1处理:接下来收到的B订单经过同样的判断后,决定要在10秒后被方法M2处理,--.这时候就可以用回调定时器来管理这些将要被延迟一定时间再执行的任务.     当然,我们可以使用定时器或前面介绍的循环引擎来实现这样的功能,只不过我们自己需要手动管理注册的定时回调任务,并且定时检查每一个未处理订单是否已经到了