摘要:转载:https://www.cnblogs.com/zhuweisky/archive/2009/09/01/1557792.html#undefined

1.缘起:

  有些系统需要每隔一段时间就执行一下某个动作,比如,一个监控系统每隔10秒钟就要检测一下被监控对象的状态是否正常,那这时我们就可以用到循环引擎了。

  有人说可以使用.NET框架自带定时器如System.Threading.Timer,嗯,没错。但是若这个类使用不当可能会引发后台池线程耗尽的后果。因为Timer的定时事件触发是在后台线程池中的某个线程中处理的。也就是说Timer的每次定时事件触发都会用到一个线程,如果定时的时间间隔小于事件处理的时间,则后台线程池中将会有越来越多的线程被Timer使用掉,直至线程池中再无空闲的线程。

而ESBasic.Threading.Engines.ICycleEngine的设计目标是永远都只使用一个线程。比如,它会隔10秒执行一个Action,执行完后再隔10秒再执行Action。间隔时间的等待与Action的执行都是在同一个线程中处理的。

循环引擎的形象示意图如下:

2.适用场合:

根据上面的描述你应该已经看到了ICycleEngine与Timer之间的区别。由于Action的执行会占用额外的时间,所以ICycleEngine不适合于精确定时的任务。比如上面的例子,下一个Action开始的时刻与上一个Action开始的时刻的真正的时间差可能是12秒,而不是10秒,因为上一个Action的执行花费了2秒。

所以,如果你的系统不需要精确的定时任务,而且又不想花费过多的精力去防范使用Timer时线程耗尽的窘境出现,那么ICycleEngine将是个不错的选择。

3.设计思想与实现

ICycleEngine接口的源码如下:

/// <summary>
/// ICycleEngine 在后台线程中进行间隔循环的引擎
/// zhuweisky 2006.12.21
/// </summary>
public interface ICycleEngine
{
/// <summary>
/// Start 启动后台引擎线程
/// </summary>
void Start(); /// <summary>
/// Stop 停止后台引擎线程,只有当线程安全退出后,该方法才返回
/// </summary>
void Stop(); /// <summary>
/// IsRunning 引擎是否运行中
/// </summary>
bool IsRunning { get; } /// <summary>
/// DetectSpanInSecs 引擎进行轮询的间隔,DetectSpanInSecs=0,表示无间隙运作引擎;DetectSpanInSecs小于0则表示不使用引擎
/// </summary>
int DetectSpanInSecs { get;set; } /// <summary>
/// OnEngineStopped 当引擎由运行变为停止状态时,将触发此事件。如果是异常停止,则事件参数为异常对象,否则,事件参数为null。
/// </summary>
event CbException OnEngineStopped;
}

  

  如何实现这个接口了?

由于不同的系统要求执行的Action不一样,所以,我们可以实现一个abstract基类BaseCycleEngine来保证循环引擎的正常运转,而派生类只要override基类的abstract方法DoDetect来执行自己的Action。

 关于BaseCycleEngine的实现要注意以下几点:

(1)循环引擎是在后台线程池的某个线程上运行的。

(2)循环引擎可以无限次的启动、停止、启动、停止……

(3)为了保证调用Stop方法时能迅速地停止引擎,我将间隔时间划分为多个BaseCycleEngine.SleepTime。而不是一次性地Sleep间隔时间。

(4)为了保证循环引擎真正停止后,才返回Stop方法的调用,我使用了ManualResetEvent来进行控制。

(5)DoDetect方法的返回值为false,则表示在该Action执行完后将停止循环引擎。此后,可以重新调用Start方法再次启动循环引擎。

4. 使用时的注意事项

(1)     要确保我们的Action(即派生类的DoDetect方法)不任何抛出异常,否则会导致循环引擎异常停止,并导致循环引擎的内部状态损坏而不可用。所以在派生类的DoDetect方法方法实现时捕捉所有的异常并加以处理。

(2)     在DoDetect方法实现中不能调用Stop方法,否则会导致死锁出现。

(3)     如果将DetectSpanInSecs设为0,则表示无间隙的执行DoDetect方法。而如果将DetectSpanInSecs设为负数,则表示不启动循环引擎。

(4)     当引擎已经启动并正在运行的过程中,如果要改变DetectSpanInSecs的值并使其生效,则必须重新启动(先调用Stop方法再调用Start方法)引擎才可。

5.扩展

(1)AgileCycleEngine

在上面的介绍中,我们都是以DoDetect方法来表示要执行的Action,而且我们必须以继承BaseCycleEngine的方式来使用循环引擎,这无疑限制了循环引擎的使用。

AgileCycleEngine的存在便是为了突破这个限制。

public sealed class AgileCycleEngine :BaseCycleEngine
{
private IEngineActor engineActor; public AgileCycleEngine(IEngineActor _engineActor)
{
this.engineActor = _engineActor;
} protected override bool DoDetect()
{
return this.engineActor.EngineAction();
}
}

AgileCycleEngine继承自BaseCycleEngine,但是它是非abstract的。AgileCycleEngine通过组合而非继承的方式来使用循环引擎,我们可以将Action的执行者抽象为一个接口IEngineActor。

public interface IEngineActor
{
/// <summary>
/// EngineAction 执行引擎动作,返回false表示停止引擎。
/// 注意,该方法不能抛出异常,否则会导致引擎停止运行(循环线程遭遇异常退出)。
/// </summary>
bool EngineAction() ;
}

通过实现IEngineActor来表明我们要执行的Action,然后将其注入到AgileCycleEngine中。

(2)永不停止的循环引擎

我们再考虑一个扩展的情况,假设我们的系统要求在启动时就将引擎运行起来,而且在整个运行的生命周期中,都不需要停止引擎,那么我们可能不想将Start方法、Stop方法暴露出来以免意外的调用Stop方法而导致引擎停止运行,那这个时候我们可以使用类似下面的技巧来做到:

public sealed class MyCircleEngine : IEngineActor
{
private AgileCycleEngine agileCycleEngine;
public void Initialize()
{
this.agileCycleEngine = new AgileCycleEngine(this);
this.agileCycleEngine.DetectSpanInSecs = ;
this.agileCycleEngine.Start();
}
#region IEngineActor 成员
public bool EngineAction()
{
// My Action
return true;
}
#endregion
}

用于示例的MyCycleEngine内部使用了AgileCycleEngine,但它没有暴露循环引擎的任何控制方法,而且Initialize方法表明MyCycleEngine只要一初始化便开始运行,而且没有办法让其停止运行。MyCycleEngine实现了IEngineActor接口,并把自己注入到AgileCycleEngine类型的成员中,于是引擎将每隔10秒钟执行一次MyCycleEngine的EngineAction方法。

Windows Service 学习系列(三)——循环引擎 ICycleEngine的更多相关文章

  1. Windows Service 学习系列(一):建立简单的Windows service

    参考:https://www.cnblogs.com/cncc/p/7170951.html 一.开发环境 操作系统:Windows 7 X64 开发环境:VS2017 编程语言:C# .NET版本: ...

  2. Windows Service 学习系列(二):C# windows服务:安装、卸载、启动和停止Windows Service几种方式

    一.通过InstallUtil.exe安装.卸载.启动.停止Windows Service 方法一 1.以管理员身份运行cmd 2.安装windows服务 切换cd C:\Windows\Micros ...

  3. MyBatis学习系列三——结合Spring

    目录 MyBatis学习系列一之环境搭建 MyBatis学习系列二——增删改查 MyBatis学习系列三——结合Spring MyBatis在项目中应用一般都要结合Spring,这一章主要把MyBat ...

  4. RabbitMQ学习系列三-C#代码接收处理消息

    RabbitMQ学习系列三:.net 环境下 C#代码订阅 RabbitMQ 消息并处理 http://www.80iter.com/blog/1438251320680361 http://www. ...

  5. scrapy爬虫学习系列三:scrapy部署到scrapyhub上

    系列文章列表: scrapy爬虫学习系列一:scrapy爬虫环境的准备:      http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_python_00 ...

  6. DocX开源WORD操作组件的学习系列三

    DocX学习系列 DocX开源WORD操作组件的学习系列一 : http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_sharp_001_docx1.htm ...

  7. .net reactor 学习系列(三)---.net reactor代码自动操作相关保护功能

    原文:.net reactor 学习系列(三)---.net reactor代码自动操作相关保护功能         接上篇,上篇已经学习了界面的各种功能以及各种配置,这篇准备学习下代码控制许可证. ...

  8. C#制作Windows service服务系列二:演示一个定期执行的windows服务及调试(windows service)

    系列一: 制作一个可安装.可启动.可停止.可卸载的Windows service(downmoon原创) 系列二:演示一个定期执行的windows服务及调试(windows service)(down ...

  9. Identity Server4学习系列三

    1.简介 在Identity Server4学习系列一和Identity Server4学习系列二之令牌(Token)的概念的基础上,了解了Identity Server4的由来,以及令牌的相关知识, ...

随机推荐

  1. oauth2.0通过JdbcClientDetailsService从数据库读取相应的配置

    oauth2.0通过JdbcClientDetailsService从数据库读取相应的配置 在上一节我们讲述的配置是把授权码存储在redis中,把相应的请求的路径用使用in-memory存储 ,这个是 ...

  2. IDEA使用总结

    IDEA常用设置 在我们第一眼看见IDEA是这个样子的: 显示工具条 我们要显示工具条!,两个按钮哦 黑色主体 我们要黑色的主题,白色的太low了! 调整字体大小 现在的字体太小了,我要鼠标滑轮+cr ...

  3. 互联网寒冬,阿里Ant Design还开坑,程序员该何去何从?

    金山都成立三十年了,不得不感叹中国在这三十年中,互联网确实是一步一步的在改变人们生活的方方面面,随着国家的发展,一大批企业搭上了互联网这趟高速列车走过了这几十年的风风雨雨,当然也造就了一批批传统行业无 ...

  4. Docker系列10—容器编排工具Docker Compose详解

    本文收录在容器技术学习系列文章总目录 1.Docker Compose 概述 Compose是一个用于定义和运行多容器Docker应用程序的工具.使用Compose,您可以使用Compose文件来配置 ...

  5. [PHP] 抽象类abstract的回顾

    1.abstract定义为抽象的类不能被实例化. 2.它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的. 3.被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实 ...

  6. Spring Boot使用AOP在控制台打印请求、响应信息

    AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等. AOP简介 AOP全称Aspect Oriented Programming,面向切面,AOP主要实现的 ...

  7. 深入理解 JavaScript 执行上下文和执行栈

    前言 如果你是一名 JavaScript 开发者,或者想要成为一名 JavaScript 开发者,那么你必须知道 JavaScript 程序内部的执行机制.执行上下文和执行栈是 JavaScript ...

  8. SpringBoot究竟是如何跑起来的?

    摘要: 神奇的SpringBoot. 原文:SpringBoot 究竟是如何跑起来的? 作者:老钱 Fundebug经授权转载,版权归原作者所有. 不得不说 SpringBoot 太复杂了,我本来只想 ...

  9. 【代码笔记】Web-CSS-CSS Border(边框)

    一,效果图. 二,代码. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...

  10. 驰骋工作流引擎JFlow与activiti的对比之4种高级分支同步模式

    多重选择(Multiple Choice) 在流程中,当一个活动完成后,有多个分支进行选择,可以选择执行其中的一个或者N个分支. 例子:比如去世博园玩,在门口检票后,可以选择A-E个片区中的N个进行观 ...