当前位置: 首页 > news >正文

闲话时间调度算法

最近一直在忙于做一个分布式的作业调度器。与通常的作业调度器不同,整个系统中没有调度中心的,所有入网的服务器都通过公共的协议协商工作。N年前的一个同事目前在一家很有前途的公司发展,前些日子开发了一套用于本公司应用产品的公共平台,其中也包括一个作业调度引擎。前几天园子里也有博友发布了开源的作业调度器。可见,作业调度在企业应用集成一天天普及的今天具有重要意义。

作业调度其中有一个重要的细节是时间调度,当作业进入调度计划时,必须按指定的时间触发。如何最大效率地、尽可能精准地、尽可能消耗最少资源地完成这个时间调度,还是很有些微妙的。其实功能非常简单:组织一个作业列表,按每个作业项指定的时间触发这个作业。例如有一组作业,其中最近的一个作业在10分钟后将被触发,在等待期间,又一个新的作业被加入队列,且应该在5分钟后将被触发。通常是立即撤销前一个计划,换成新的计划。所以,这个时间调度一定要能够撤销某项计划,适应动态变化的需要。

WM_TIMER

定时消息是一个选择,唯一依赖的是一个消息循环。可惜服务器通常不依赖消息循环来工作。虽然可以创建一个窗口或线程并建立消息循环,但是很显然,这是一个串行化的过程,有悖于服务器通常所要求的可伸缩性。

Sleep函数或其它等候函数的超时

睡眠函数就是让CPU沉睡一段时间。沉睡多久?上个世纪的某个时候我真的这么用过,用MsgWaitForMultipleObjects函数来处理延时。为了防止超时值被不断插入的处理所破坏,我采用逐步逼近法,就是每次等候函数的超时值为需要延时的时间的一半多一点儿,在触发时再进行调整,直到满足时间精度要求时触发作业。

WaitableTimer

可等待的定时器是一个优于定时器消息的选择,毕竟这是一个内核对象,控制自如,可用在任何线程中。虽然WaitableTimer也是跨进程的,不过我建议还是不要这么前卫了,毕竟你无法访问另外一个进程的内存。

算法

一个作业项配备一个WaitableTimer?这样可以很轻松地令系统崩溃,因而这种想法很对对Windows有仇恨的程序员的味口。所以,用单个WaitableTimer再配以一个线程安全的列表是非常合适的。当有新的作业加入时,立即取消从前的定时,重排作业表,将需要最先被触发的作业挑出来,如果已经超时,则立即触发并让别的线程有机会执行这个作业,然后再挑下一个需要最先被触发的作业,如此往复下去直到再挑不出需要立即触发的作业为止;如果发现需要等待一段时间再触发的作业,就按该作业的要求来重新设定WaitableTimer;如果没有了需要触发的作业,也将WaitableTimer的时间间隔设置为第二天的同一时刻。当WaitableTimer被触发时,所需要做的仅仅是同一件事情,继续挑出需要立即触发的作业,然后如此地往复下去。

最后需要考虑的事情就是效率了。时间值是一个比较大的结构,可以采用64位的负数或者正的浮点数,前者为相对时间,后者为绝对时间,当作业项多的时候,即使是采用二分法还是需要花费比较多的CPU时间去比较。其实我并不是单纯为了节省CPU时间,而是担心因为耗费了额外的CPU时间而导致作业被延误。有更快的算法么?将时间值用两个无符号的32位整数来表示,比较的时间仅仅只需要做两次寄存器的减法。第一个无符号的整数是当前的TickCount,以毫秒为单位。当然,这个很容易溢出,因为一个32位整数只能表示4294967295毫秒,也就是49.7个自然天。所以就需要另外一个32位整数了,这个整数仅仅用来记录轮次(你已经知道了,每个轮次表示49.7)。这个轮次很容易调整。如果目前取出来的TickCount比上一个TickCount小则说明计数器已经复位过,已经进入一个新的轮次。需要两个额外的32位无符号整数来记录最后的校准值。所以,作业项的时间刻度需要一个结构来记录,该结构记录作业进入时的TickCount和轮次,再记录需要延迟的毫秒数。这样,所记录的信息与当前TickCount和轮次进行比较很容易明确作业是否已经超时。采用TickCount的另外一个意义是这个API效率高于GetSystemTime,别小看这一点差异,因为这几个API调度频度极高,差异就积少成多了。现在是否已经明白在发现作业列表中没有任何作业的时候为什么还要设置定时器了么?是的,仅仅只是重新校准一下而已。当然,我承认设置1天和49天是没有什么太大的差异的。但是,设置50天就有本质差异了,没准你就有中六合彩的彩头,刚好在这个空档,TickCounter复位了!获得这么好运气的机率是:

(50*24*60*60*1000-4294967296)/ 4294967296=5.8

虽然很小,但的确大于0。

转载于:https://www.cnblogs.com/Barton131420/archive/2007/08/29/874531.html

相关文章:

  • 搜索引擎技术文章
  • Lucene.net 实现全文搜索(转)
  • [文摘20071010]没女
  • 用XML和SQL 2000来管理存储过程调用
  • SharePoint Server 2007 页面模型
  • GridView
  • ASP
  • 新来到出转转
  • 在.net中创建使用全球唯一标识符
  • 8. Automatic Properties(自动属性)
  • C#命名规范
  • 一条常见的行合并问题(SQL)
  • 自己动手做的LED摄影灯
  • Ajax从入门到精通!!
  • 支持函数,变量的算术表达式计算(二、中缀转后缀)
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • 【React系列】如何构建React应用程序
  • AHK 中 = 和 == 等比较运算符的用法
  • angular组件开发
  • Brief introduction of how to 'Call, Apply and Bind'
  • js面向对象
  • Vue UI框架库开发介绍
  • 从零开始在ubuntu上搭建node开发环境
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • 移动互联网+智能运营体系搭建=你家有金矿啊!
  • 正则表达式小结
  • ionic入门之数据绑定显示-1
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • scrapy中间件源码分析及常用中间件大全
  • ​插件化DPI在商用WIFI中的价值
  • # 计算机视觉入门
  • #HarmonyOS:Web组件的使用
  • %check_box% in rails :coditions={:has_many , :through}
  • (¥1011)-(一千零一拾一元整)输出
  • (ctrl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (附源码)springboot工单管理系统 毕业设计 964158
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (译)2019年前端性能优化清单 — 下篇
  • (转) Android中ViewStub组件使用
  • ***测试-HTTP方法
  • ***微信公众号支付+微信H5支付+微信扫码支付+小程序支付+APP微信支付解决方案总结...
  • ./mysql.server: 没有那个文件或目录_Linux下安装MySQL出现“ls: /var/lib/mysql/*.pid: 没有那个文件或目录”...
  • .dat文件写入byte类型数组_用Python从Abaqus导出txt、dat数据
  • .htaccess 强制https 单独排除某个目录
  • .NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
  • .NET中使用Protobuffer 实现序列化和反序列化
  • @data注解_SpringBoot 使用WebSocket打造在线聊天室(基于注解)
  • @selector(..)警告提示
  • @在php中起什么作用?
  • [2021ICPC济南 L] Strange Series (Bell 数 多项式exp)
  • [EULAR文摘] 利用蛋白组学技术开发一项蛋白评分用于预测TNFi疗效
  • [Flex][问题笔记]TextArea滚动条问题