DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: hermithero44
今日帖子: 15
在线用户: 23
导航: 论坛 -> 开源项目 斑竹:joki,ralf_jones  
作者:
男 chineseswish (swish) ▲▲▲▲▲ -
普通会员
2015/8/12 10:01:46
标题:
[QWorker] 使用 QWorker 做为计划任务引擎 浏览:2100
加入我的收藏
楼主: 转载自QDAC官网网站,文章发现问题可能随时更新,最新版本请访问 http://blog.qdac.cc/?p=2794

QWorker 提供了 Plan 函数来提供计划任务功能的支持。每个任务做为一个作业,可以在指定的时间点被触发执行。而 cron 作为 Linux 操作系统下计划任务的标准被广大用户所熟知,QWorker 也就不再闭门造车,决定遵从这一规则。但是,可但是,通过百度后,你会发现有两个,一个是 crontab 标准,精确到分钟,另一个是 java 的 spring 框架使用的仿 cron 格式,精确到秒。在一翻无谓的挣扎和犹豫之后,最终决定使用 spring 兼容的实现,原来基于 cron 的实现被推倒重新实现。

好了,确定了目标并实现之后,我们就是实践了。计划任务作业的创建与普通的作业其实并没有什么不同,如果非要说有啥不同,那就是计划任务作业的接口函数名为 Plan,并且使用了一个 TQPlanMask 类型的参数来传递计划任务的调度计划。

先来看看 Plan 函数的声明:


function Plan(AProc: TQJobProc; const APlan: TQPlanMask; AData: Pointer;ARunInMainThread: Boolean = False;AFreeType: TQJobDataFreeType = jdfFreeByUser): IntPtr; overload;
function Plan(AProc: TQJobProc; const APlan: QStringW; AData: Pointer;ARunInMainThread: Boolean = False;AFreeType: TQJobDataFreeType = jdfFreeByUser): IntPtr; overload;
function Plan(AProc: TQJobProcG; const APlan: TQPlanMask; AData: Pointer;ARunInMainThread: Boolean = False;AFreeType: TQJobDataFreeType = jdfFreeByUser): IntPtr; overload;
function Plan(AProc: TQJobProcG; const APlan: QStringW; AData: Pointer;ARunInMainThread: Boolean = False;AFreeType: TQJobDataFreeType = jdfFreeByUser): IntPtr; overload;
{$IFDEF UNICODE}
function Plan(AProc: TQJobProcA; const APlan: TQPlanMask; AData: Pointer;ARunInMainThread: Boolean = False;AFreeType: TQJobDataFreeType = jdfFreeByUser): IntPtr; overload;
function Plan(AProc: TQJobProcA; const APlan: QStringW; AData: Pointer;ARunInMainThread: Boolean = False;AFreeType: TQJobDataFreeType = jdfFreeByUser): IntPtr; overload;
{$ENDIF}
好了,简单说下几个参数都干啥的,AProc、AData、ARunInMainThread、AFreeType 这四个参数与普通的作业并没有任何不同,依然是作业处理函数、附加数据指针、是否运行在主线程、附加数据指针的释放方式,唯一多出来的参数就是 APlan,从上面的声明可以看出来,我们提供了两个重载,一个是 TQPlanMask 类型,另外一个是直接的 QStringW,我们可以根据需要选择一个格式:

TQPlanMask 格式可以提供更多额外的控制,比如计划任务的首次执行时间范围。
如果没有额外的控制需要,QStringW 格式是直接设置作业调度的掩码,明显更方便一些。
下面我们看调用的一个例子:

Workers.Plan(DoPlanJob, '0 * * * * * "每分钟重复一次的作业"', nil, True);

这样一句代码就定义了一个计划任务,要求在每分钟执行一次作业 DoPlanJob。为什么呢,我们就要从这个计划任务的格式掩码说起。

QWorker 的计划任务掩码参考自 Spring ,共有 6-7 部分组成,格式如下:

秒 分 时 日 月 周 [年]

其中,年是可选,没有的话,就忽略它的检查。第一个部分都有自己的取值范围和为了方便条件设定的通配符,年、月、日和时、分、秒我觉得就不用细说取值范围了,相信你也知道,只是特别说明以下几点:

小时使用的是  24 小时制,所以范围是0-23;
月可以使用月份的英文缩写,如 JAN、MAY 等;
周是以周日为起点,定义为 1 ,剩下的依次类推,也就是说周六为7,当然更清晰的是使用星期的英文缩写,如 SUN、WED 等;
年份的表示范围是 1970~9999,不过这个估计很少用了。
补充一点,月或周使用英文时不区分大小写,所以 JAN 和 jAn 的结果是一样一样的。
然后我们说一下通配符。所有的部分都支持 -、* 和 / 三种,日部分还额外支持 L、W 和 ?,周额外支持 L 和 ?。下面说一下各个通配符的含义:

*
表示所有值. 例如:在分的字段上设置 “*”,表示每一分钟都会触发;
?
表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为“?”,具体设置为 0 0 0 10 * ?;

表示区间。例如 在小时上设置“10-12”,表示 10、11、12 点都会触发;
,
表示指定多个值,例如在周字段上设置 “MON,WED,FRI” 表示周一,周三和周五触发;
/
用于递增触发。如在秒上面设置 “5/15”  表示从 5 秒开始,每增 15 秒触发(5、20、35、50)。 在月字段上设置 “1/3” 所示每月 1 号开始,每隔三天触发一次;
L
表示最后的意思,取英文 Last 的缩写。在日字段设置上,表示当月的倒数第几天(依据当前月份,如果是二月还会依据是否是闰年),如 L-2 代表从月份的最后一天开始倒数两天,L0 时,0可以省略,代表月份的最后一天。 在周字段上表示星期六,相当于 “7” 或 “SAT”。如果在 “L” 前加上数字,则表示该月最后一个星期几。例如在周字段上设置 “6L” 这样的格式,则表示 “本月最后一个星期五”;
W
表示离指定日期的最近那个工作日(默认为周一至周五,你可以重新实现一个函数,然后设计 IsWorkDay 函数指针指向它来个性化工作日设置)。例如在日字段上设置 “15W”,表示离每月 15 号最近的那个工作日触发。如果 15 号正好是周六,则找最近的周五(14号)触发,,如果 15 号是周未,则找最近的下周一(16号)触发。如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 “1W”,它则表示每月 1 号往后最近的工作日触发。如果 1 号正是周六,则将在 3 号下周一触发。(注,”W”前只能设置具体的数字,不允许使用区间 “-”);
#
序号(表示每月的第几个周几),例如在周字段上设置“6#3”,表示在每月的第三个周六。注意如果指定 “n#5”,而正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了) ;
好了,格式的说明也就这样子了,提示下 L 和 W 可以一起组合使用。如果在日字段上设置 LW ,则表示在本月的最后一个工作日触发。

然后我们提供一些简单的掩码示例:

0 0 12 * * ? 每天12点触发
0 15 10 ? * * 每天10点15分触发
0 15 10 * * ? 每天10点15分触发
0 15 10 * * ? * 每天10点15分触发
0 15 10 * * ? 2005 2005年每天10点15分触发
0 * 14 * * ? 每天下午的 2点到2点59分每分触发
0 0/5 14 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发)
0 0/5 14,18 * * ? 每天下午的 2点到2点59分、18点到18点59分(整点开始,每隔5分触发)
0 0-5 14 * * ? 每天下午的 2点到2点05分每分触发
0 10,44 14 ? 3 WED 3月分每周三下午的 2点10分和2点44分触发
0 15 10 ? * MON-FRI 从周一到周五每天上午的10点15分触发
0 15 10 15 * ? 每月15号上午10点15分触发
0 15 10 L * ? 每月最后一天的10点15分触发
0 15 10 ? * 6L 每月最后一周的星期五的10点15分触发
0 15 10 ? * 6L 2002-2005 从2002年到2005年每月最后一周的星期五的10点15分触发
0 15 10 ? * 6#3 每月的第三周的星期五开始触发
0 0 12 1/5 * ? 每月的第一个中午开始每隔5天触发一次
0 11 11 11 11 ? 每年的11月11号 11点11分触发(光棍节)
----------------------------------------------
QDAC 开源数据库访问组件欢迎大家关注讨论和使用 官网(博客):http://www.qdac.cc ;讨论QQ群:250530692 QLang 多语言组件快速让你的程序走向海外
作者:
男 nevergrief (孤独骑士) ★☆☆☆☆ -
盒子活跃会员
2015/8/12 16:03:50
1楼: 用什么实现触发的?定时器?
----------------------------------------------
只有偏执狂才能生存!
作者:
男 eric685 (泛舟客) ▲▲△△△ -
注册会员
2015/8/12 17:53:36
2楼: 支持波哥!
----------------------------------------------
-
作者:
男 zhahongyi (如风) ▲▲▲▲▲ -
普通会员
2015/8/12 23:14:46
3楼: 顶下!
----------------------------------------------
-
作者:
男 chineseswish (swish) ▲▲▲▲▲ -
普通会员
2015/8/13 9:18:45
4楼: 根据昨天群里的调查结果,更新了周的规则,改为周一为 1 ,周日为 0 或 7,其它规则不变,文章最新版本官网已经更新。
----------------------------------------------
QDAC 开源数据库访问组件欢迎大家关注讨论和使用 官网(博客):http://www.qdac.cc ;讨论QQ群:250530692 QLang 多语言组件快速让你的程序走向海外
作者:
男 chineseswish (swish) ▲▲▲▲▲ -
普通会员
2015/8/13 9:19:43
5楼: @1楼 nevergrief
==========
基于 QWorker 的定时任务实现的,直接调用 QWorker.Plan 函数就可以。
----------------------------------------------
QDAC 开源数据库访问组件欢迎大家关注讨论和使用 官网(博客):http://www.qdac.cc ;讨论QQ群:250530692 QLang 多语言组件快速让你的程序走向海外
作者:
男 mlskin (mlskin) ▲▲△△△ -
注册会员
2015/8/13 9:39:25
6楼: Nice~~~~用了段时间QWoker感觉不错,用它处理后台任务变得非常简单,很实用
另外问一下,QWoker的信号触发功能可以支持跨进程触发么?
----------------------------------------------
Delphi MlSkin 它能让你的程序拥有像QQ一样多彩炫丽的外观http://www.pngui.com
作者:
男 chineseswish (swish) ▲▲▲▲▲ -
普通会员
2015/8/13 10:07:17
7楼: @mskin
======
不支持跨进程触发,这个跨进程需要额外的一些支持,目前还未实现。将来会支持跨进程和跨主机触发,但那是很久后的事了。
----------------------------------------------
QDAC 开源数据库访问组件欢迎大家关注讨论和使用 官网(博客):http://www.qdac.cc ;讨论QQ群:250530692 QLang 多语言组件快速让你的程序走向海外
作者:
男 chineseswish (swish) ▲▲▲▲▲ -
普通会员
2015/8/13 10:08:26
8楼:
补充:[QWorker] 计划任务时,如果使用自己的工作日判定规则 http://blog.qdac.cc/?p=2800

QWorker 中默认的工作日判定规则是周一至周五为工作日,周六和周日为休息日,大多数情况下,这没有多大的问题。但是,可但是,无论那个国家,都有自己的法定节假日,将它们放在工作日里显示是不合适的。而做为一个底层的引擎,显示不适合只根据中国的规则来写死。反过来,你也没法写死,因为像我们国家,许多法定节假日是按照农历走的,在阳历上的日期并不固定,显然这没法写死。

所以,在 QWorker 中,提供了一个全局的 IsWorkDay 变量,用于关联到用户自定义的函数来判断指定的日期是否是工作日。其默认关联到了 DefaultIsWorkDay 函数,我们要改变它的行为,只需要编写一个自己的节假日判断函数,然后在程序启动时,设置 IsWorkDay 为自己的判断函数就可以了。

这里介绍另一个东东:TQBits,你可以用它来最小化来存贮每年的休息日数据,每一位代表一天,然后 366 天存贮只需要 366 / 8 约 46 个字节(可提供368位)的大小即可。下面是一个简单的例子,其中 WorkDayMasks 是假设预存贮好的 TQBits 数组,每年对应 46 字节的工作日信息数据。

var
  WorkDayMasks:array[2015..2015] of TQBits;
...
function MyIsWorkDay(ADate: TDateTime): Boolean;
var
  AYear:Word;
begin
AYear:=YearOf(ADate);
if (AYear>=Low(WorkDayMasks)) and (AYear<=High(WorkDayMasks)) then
   Result:=WorkDayMasks[AYear][DayOfTheYear(ADate)]
else
   Result:=DefaultIsWorkDay(ADate);
end;
然后你在调用 QWorker.Plan 时的掩码中,如果包含了 W 掩码的时候,就会通过这个函数来判定某一天是否是工作日,从而决定是否调度作业。
----------------------------------------------
QDAC 开源数据库访问组件欢迎大家关注讨论和使用 官网(博客):http://www.qdac.cc ;讨论QQ群:250530692 QLang 多语言组件快速让你的程序走向海外
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v2.1 版权所有 页面执行46.875毫秒 RSS