`
feipigwang
  • 浏览: 775362 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多
1.缘起:

举个例子也许就能够说清楚回调定时器的用途。假设我的订单系统接收各种不同类型的订单,当订单A进来时,系统根据订单的类型和其它特征进行综合判断后,决定A订单要在2秒之后被方法M1处理;接下来收到的B订单经过同样的判断后,决定要在10秒后被方法M2处理,……。这时候就可以用回调定时器来管理这些将要被延迟一定时间再执行的任务。

当然,我们可以使用定时器或前面介绍的循环引擎来实现这样的功能,只不过我们自己需要手动管理注册的定时回调任务,并且定时检查每一个未处理订单是否已经到了处理的时刻。而回调定时器已经自动帮我们做好了这些事情,而且还是一个与具体应用无关的通用组件,我们不需要再重复实现一个特定的类来做这件事情。

回调定时器的形象示意图如下:



2.
适用场合:

设计回调定时器ESBasic.Threading.Timers.ICallbackTimer的主要是为了解决类似下面的问题:

(1) 任务(回调)需要被延迟某一个时间间隔后执行。

(2)不同的任务需要被延迟的时间间隔可能不同。

(3)不同的任务需要被处理的方式可能不同。

(4)回调的延迟不需要非常精确。

回调定时器要解决的最主要问题是第一点,后面两点也是回调定时器支持的重要特性。

3.设计思想与实现

ICallbackTimer接口的定义如下:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> ///<summary>
///ICallbackTimer回调定时器。
///注意:回调任务会异步在ThreadPool的WorkerThread上执行。即使目标任务抛出异常也不会影响INotifyTimer的继续运行。
///</summary>
publicinterfaceICallbackTimer<T>:ICycleEngine
{
intTaskCount{get;}

///<summary>
///AddCallback添加一个回调任务。目标任务会在spanInSecs后运行。仅仅运行一次。
///</summary>
///<paramname="spanInSecs">多少秒后执行任务</param>
///<paramname="_callback">目标方法的委托</param>
///<paramname="_callbackPara">调用目标方法的参数</param>
///<returns>新的任务编号</returns>
intAddCallback(intspanInSecs,CbGeneric<T>_callback,T_callbackPara);

///<summary>
///RemoveCallback删除目标回调任务。
///</summary>
voidRemoveCallback(inttaskID);

///<summary>
///RemoveCallbackAndAddNew删除目标回调任务,并添加一个新的回调任务。
///</summary>
intRemoveCallbackAndAddNew(inttaskIDToRemoved,intspanInSecs,CbGeneric<T>_newCallback,T_newCallbackPara);

///<summary>
///GetLeftSeconds离目标任务被回调执行还有多长时间(s)。返回0,表示任务不存在或者任务已经被执行。
///</summary>
intGetLeftSeconds(inttaskID);

///<summary>
///Clear清除所有回调任务。
///</summary>
voidClear();
}

根据上述对回调定时器的描述,我们可以借助循环引擎来实现它。你已经看到,ICallbackTimer继承了ICycleEngine接口,这说明我们可以通过StartStop方法来控制回调定时器的运行,并通过DetectSpanInSecs属性来设置检测任务状态的时间间隔,当然,DetectSpanInSecs设置的值最好小于最小的回调任务的延迟时间间隔。

ICallbackTimer接口的泛型参数T,代表的是回调执行时所用到的参数的类型。而回调方法的签名必须是只接受一个T类型的参数,并且没有返回值(即如泛型委托CbGeneric<T>)。

AddCallback方法返回一个int,表示添加的任务的唯一编号,我们可以通过这个编号来查询该任务离被回调执行的时间(GetLeftSeconds方法),或者根据该编号来取消目标回调任务的执行(RemoveCallback方法)。

CallbackTimer实现了ICallbackTimer接口,其实现要注意以下几点:

(1)CallbackTimer继承自BaseCycleEngine,它借助于循环引擎来进行任务状态的循环检测。

(2)为了允许在多线程的环境中回调定时器,CallbackTimer必须对内部集合(dicTask)进行加锁控制。

(3)CallbackTimer使用CallbackTask类来将一个定时回调任务封装起来。

(4)回调任务是异步在后台的ThreadPoolWorkerThread上执行的。所以达到执行条件的多个回调任务不会相互阻塞,而是几乎同时执行的。

(5)DoDetect方法的实现中,我们先拷贝一份任务列表,然后再对其作foreach,而不是直接this.dicTask.Valuesforeach,这是因为,如果在某个回调执行时,调用了AddCallback/RemoveCallback将修改this.dicTask的内容,而此时对this.dicTask.Valuesforeach还未结束,这时foreach将抛出异常。

4. 使用时的注意事项

首先,是要注意GetLeftSeconds返回的值是不精确的。因为CallbackTimer是在每次循环检测的时候(覆写基类的DoDetect方法),修改每个CallbackTaskLeftSeconds的(通过CallbackTaskSecondsPassed方法)。所以,GetLeftSeconds方法返回的只是一个大概的而非精确的值。

其次,回调的执行是“一次性的”,即注册的一个回调任务只会被执行一次,或者被取消。

再次,回调任务定时器允许回调任务执行时抛出异常,只不过该异常会被回调定时器忽略。所以,如果你要处理该异常,就应该在回调方法中捕获这个异常。

最后,由于回调任务是异步在后台线程池中运行的,所以如果同时被执行的回调的任务很多,其数量超过了后台线程池中的线程数量,此时就会导致某些回调任务将会被进一步延迟执行。

5.扩展

回调定时器ICallbackTimer暂时没有任何扩展。

注:ESBasic源码可到http://esbasic.codeplex.com/下载。

分享到:
评论

相关推荐

    asynchronous timer_异步定时器_

    在多线程编程中,异步定时器通常通过创建一个单独的线程来实现,这个线程负责监控时间,并在预定的时间点触发预定义的回调函数或事件。以下是一些关键知识点: 1. **线程基础**:首先,要理解多线程的概念。线程是...

    游戏服务器之Timer计时器(定时器)动态链接库

    在游戏服务器开发中,Timer计时器(定时器)是一个至关重要的组件,它负责调度服务器上的各种时间依赖性任务,如心跳检测、周期性更新、超时处理等。本动态链接库(DLL)专为游戏服务器设计,提供高效且可靠的定时...

    VC学习大纲 VC学习讲义

    - **回调函数**:回调函数是在特定条件下由系统自动调用的函数,例如窗口过程函数。 - **窗口关闭与应用程序退出的工作关系**:窗口关闭时,应用程序可能并不会立即退出,这取决于程序的设计。 - **使用VC++的若干小...

    前端开源库-serial-loop

    1. **异步编程**:在JavaScript中,异步编程通常通过回调函数、Promise、async/await等方式实现,用于处理非阻塞操作,如Ajax请求、定时器等,以避免程序因等待I/O操作完成而停滞。 2. **串行执行**:在异步编程中...

    MFC时钟和数字

    当定时器触发时,OnTimer回调函数会被调用。 3. **绘制时钟**:在OnDraw函数中,你需要获取当前时间(通过CTime或COleDateTime类),然后根据小时、分钟和秒来绘制时钟盘面。你可以使用CRect、CBrush和CPen等类来...

    mfc精确控制时间 ms级别

    如果需要持续的定时任务,例如每隔一定毫秒执行一次回调,可以考虑使用Windows的消息队列和消息循环。创建一个自定义的消息类型,并在消息处理函数中调用`Sleep`函数来延迟指定的时间。不过这种方法的精度仍然受到`...

    VC6 MFC 非常好的仪器仪表界面模拟项目,仪表,温度计等的控制与动态显示

    例如,使用MFC的命令ID来处理用户操作,而不是直接在控件的回调函数中处理业务逻辑。此外,可以考虑采用MFC的文档模板(CDocTemplate)和多文档界面(MDI)来支持多个仪表的并行查看和操作。 最后,考虑到项目的...

    贪吃蛇_java_贪吃蛇tanchishe_

    蛇的移动通常在一个定时器的回调函数中进行,定时器可以使用javax.swing.Timer类。碰撞检测则要检查蛇的身体是否与自身或其他障碍物(如墙壁)重叠。如果发生碰撞,游戏结束;否则,蛇的长度增加,食物消失,得分...

    ### 这是一篇对Qt开发进行了详细概述的文章

    这种机制避免了回调函数的复杂性,简化了程序的设计和维护。 **1.2 学习资源** 对于初学者来说,可以从官方文档开始学习Qt的基础知识,同时也可以参考书籍如《C++ GUI Programming with Qt 4》或在线课程来加深...

    【MFC】滚动字幕的状态栏.rar

    创建一个定时器(Ctimer),并在其回调函数中改变滚动位置,然后调用RedrawWindow()或InvalidateRect()来刷新状态栏的部分。通过控制滚动速度和方向,可以模拟出字幕从一端向另一端移动的效果。 同时,我们还需要...

    C事件驱动循环的网络IO编程框架源代码

    当一个事件发生时,事件循环会调用相应的回调函数来处理这个事件。这种编程模型特别适用于需要处理大量并发连接的服务器,如Web服务器、聊天服务器等。 在这个框架中,"VC/C++源码"表示代码是用Microsoft的Visual ...

    cs-programming:C#提高游戏编程的编程能力

    在游戏逻辑中,可以利用它们快速定义回调函数,如碰撞检测后的处理或定时器事件。 四、泛型 泛型允许你创建可重用的类型安全的代码,无需针对每种数据类型都编写独立的实现。在游戏编程中,泛型可以应用于游戏对象...

Global site tag (gtag.js) - Google Analytics