维基百科上是这样描述计划任务的:

“Cron is a time-based job scheduler in Unix-like computer operating systems. Cron is short for Chronograph. Cron enables users to schedule jobs (commands or shell scripts) to run automatically at a certain time or date.”

有些场景下你需要定时的重复调度某些操作,下面是一些定时的例子:

  • 每15分钟
  • 每0,15,30,45 分钟
  • 每天12 点
  • 每天的12AM, 6AM, 12PM, 6PM
  • 星期六的 12PM
  • 每个月的第一天

在这片文章里,我将会介绍使用C#实现计划任务。

前人做的工作

当我在工作中遇到这样的需求时,我的第一想法就是去网上看看,不是有句话嘛,不要重复发明轮子。根据网上的资料显示很多人已经在C#实现计划任务上做了很多工作。但是多数不符合我的要求,还有一部分实现的比较复杂。我就想做一个干干净净、清清爽爽的库,自己用。下面是我参考比较多的两个库。

  • Quartz.NET – 这个库比较全,其实他是一个线程调度库,比我要求的要全得多。
  • NCrontab – 这个库比较简单,实现的是一个工作调度器,缺少解析cron的部分,不适合我使用。

我在NCrontab的基础上增加了cron表达式解析功能,使用事件触发完成预设工作的执行,完成了我的计划任务库。

如何使用我的代码库?

下面举一个例子来说明如何使用我的代码库完成建立一个计划任务:

// Create the cron schedule.
//
CronExpression cronExpression = CronBuilder.CreateHourlyTrigger(new int[] {0, 30});
CronSchedule cronSchedule = CronSchedule.Parse(cronExpression.ToString());
List<CronSchedule> cronSchedules = new List<CronSchedule> { cronSchedule }; // Create the data context for the cron object.
//
CronObjectDataContext dc = new CronObjectDataContext
{
Object = myObject,
CronSchedules = cronSchedules,
LastTrigger = DateTime.MinValue
}; // Create the cron object.
//
CronObject cron = new CronObject(dc); // Register for events.
//
cron.OnCronTrigger += Cron_OnCronTrigger; // Start the cron job.
//
cron.Start();

首先建立一个调度器,在每小时的0分钟和30分钟触发事件,事件响应函数里面可以做多项工作。.  ‘CronBuilder’ 类可以辅助你建立计划任务,你也可以使用cron表达式完成计划任务的建立,上面例子中的任务可以使用 “0,30 * * * *” 或者 “*/30 * * * *”表示,相比之下使用CronBuilder使得工作更简单。

当计划任务启动后,到了指定时间‘OnCronTrigger’事件将会触发,事件响应函数将会执行:

private void Cron_OnCronTrigger(CronObject cronObject)
{
IRunnable myObject = cronObject.Object as IRunnable;
myObject.Run();
}

cronObject类表示计划工作,其中包含可执行的方法。

调度器Scheduler

‘CronSchedule’ 类表示调度器,其Parse方法解析计划任务表达式。这个类有一个私有构造函数,使用重载的Parse方法可以实例化一个新的实例。

其中调度的算法是首先计算出计划时间与当前时间的间距,然后在当前一分钟内自旋等待,然后检查是否到达触发时刻,如果没有将已经自旋时间增加一分钟,继续下一分钟自旋,自旋满一个小时后,增加已经自旋的小时数,如此递增,自旋时间等于已经算出的时间间距。

基于事件的任务触发

‘CronObjectDataContext 类包含三个属性.

  • CronSchedules – 一个事件可以包含多个调度器,也即是可以有多重触发机制.
  • LastTrigger – 这里记录了上次触发的时间.
  • Object – 此属性表示将要执行的任务.

‘CronObject’类包含两个公开方法:  Start 和 Stop。用来启动和停止任务触发:

public bool Start()
{
lock (_startStopLock)
{
// Can't start if already started.
//
if (_isStarted)
{
return false;
}
_isStarted = true;
_isStopRequested = false; // This is a long running process. Need to run on a thread
// outside the thread pool.
//
_thread = new Thread(ThreadRoutine);
_thread.Start();
} // Raise the started event.
//
if(OnStarted != null)
{
OnStarted(this);
} return true;
} public bool Stop()
{
lock (_startStopLock)
{
// Can't stop if not started.
//
if (!_isStarted)
{
return false;
}
_isStarted = false;
_isStopRequested = true; // Signal the thread to wake up early
//
_wh.Set(); // Wait for the thread to join.
//
if(!_thread.Join(5000))
{
_thread.Abort(); // Raise the thread abort event.
//
if(OnThreadAbort != null)
{
OnThreadAbort(this);
}
}
} // Raise the stopped event.
//
if(OnStopped != null)
{
OnStopped(this);
}
return true;
}.

线程routine主要负责触发OnCronTrigger事件,其中使用EventWaitHandle来实现在下一个调度触发来临前线程等待。在Stop方法中也是使用这种机制来保证所有调度器全部停止的。

private readonly EventWaitHandle _wh = new AutoResetEvent(false);
private void ThreadRoutine()
{
// Continue until stop is requested.
//
while(!_isStopRequested)
{
// Determine the next cron trigger
//
DetermineNextCronTrigger(out _nextCronTrigger); TimeSpan sleepSpan = _nextCronTrigger - DateTime.Now;
if(sleepSpan.TotalMilliseconds < 0)
{
// Next trigger is in the past. Trigger the right away.
//
sleepSpan = new TimeSpan(0, 0, 0, 0, 50);
} // Wait here for the timespan or until I am triggered
// to wake up.
//
if(!_wh.WaitOne(sleepSpan))
{
// Timespan is up...raise the trigger event
//
if(OnCronTrigger != null)
{
OnCronTrigger(this);
} // Update the last trigger time.
//
_cronObjectDataContext.LastTrigger = DateTime.Now;
}
}
}

DetermineNextCronTrigger方法决定哪个调度器将被拿出来负责调度。

private void DetermineNextCronTrigger(out DateTime nextTrigger)
{
nextTrigger = DateTime.MaxValue;
foreach (CronSchedule cronSchedule in _cronObjectDataContext.CronSchedules)
{
DateTime thisTrigger;
if(cronSchedule.GetNext(LastTigger, out thisTrigger))
{
if (thisTrigger < nextTrigger)
{
nextTrigger = thisTrigger;
}
}
}
}

累死我了,终于说完了。大家可是试用一下。

猛击下载链接

转载请注明:源自极客飞逸(http://www.fiiii.com

使用C#实现计划任务(corn job)的更多相关文章

  1. C#编写一个在asp.net core 3.1下的简单的corn模式的计划任务和一个更简单的定时器类

    asp.net core 下,新增了一个BackgroundService用来实现能在后台跑一个长久运行的任务,因此,也可以用来替换掉原来使用的static的Timer组件, Timer组件主要有以下 ...

  2. corn计划周期任务

                                                                             corn计划任务 1.计划任务有四种方式   cron ...

  3. Quartz中时间表达式的设置-----corn表达式

    Quartz中时间表达式的设置-----corn表达式 时间格式: <!-- s m h d m w(?) y(?) -->,   分别相应: 秒>分>小时>日>月 ...

  4. Quartz中时间表达式的设置-----corn表达式 (转)(http://www.cnblogs.com/GarfieldTom/p/3746290.html)

    Quartz中时间表达式的设置-----corn表达式 (注:这是让我看比较明白的一个博文,但是抱歉,没有找到原作者,如有侵犯,请告知) 时间格式: <!-- s m h d m w(?) y( ...

  5. Quartz中时间表达式的设置-----corn表达式 (转)

    Quartz中时间表达式的设置-----corn表达式 (注:这是让我看比较明白的一个博文,但是抱歉,没有找到原作者,如有侵犯,请告知) 时间格式: <!-- s m h d m w(?) y( ...

  6. webapi + windows计划 + mshta 实现定时执行任务

    当然,实现定时任务有更好的操作方式,比如方式一:asp.net mvc+quartz.net +corn +webapi,asp.net mvc做任务管理的平台,使用CronTrigger做定时触发, ...

  7. 在Spring3中使用注解(@Scheduled)创建计划任务

    Spring3中加强了注解的使用,其中计划任务也得到了增强,现在创建一个计划任务只需要两步就完成了: 创建一个Java类,添加一个无参无返回值的方法,在方法上用@Scheduled注解修饰一下: 在S ...

  8. 状态压缩dp初学__$Corn Fields$

    明天计划上是要刷状压,但是作为现在还不会状压的\(ruoruo\)来说是一件非常苦逼的事情,所以提前学了一下状压\(dp\). 鸣谢\(hmq\ juju\)的友情帮助 状态压缩动态规划 本博文的大体 ...

  9. Linux centos7 linux任务计划cron、chkconfig工具、systemd管理服务、unit介绍、 target介绍

    一.linux任务计划cron crontab -u  -e -l -r 格式;分 时 日 月 周 user command 文件/var/spool/corn/username 分范围0-59,时范 ...

随机推荐

  1. Linq中的group by多表多字段,Sum求和

    //Line to Sql 写法 var data = (from a in Items group a by new { a.GroupId, a.Id } into b //orderby new ...

  2. maven 项目 配置docker镜像生成(dockerfile-maven-plugin)

    插件地址:https://github.com/spotify/dockerfile-maven 依github上备注,只要在项目根上录上编写dockerfile,然后引用插件即可 编写Dockerf ...

  3. mybatis 遍历字段和字段对应的值循环插入

    java代码: /** * 插入单条数据 * @param request * @param response * @param id * @param str * @return * @throws ...

  4. Experimental Educational Round: VolBIT Formulas Blitz F

    Description One company of IT City decided to create a group of innovative developments consisting f ...

  5. git学习中遇到的疑难杂症

    GIT仓库如何恢复到前一次提交 一.通过使用Git版本恢复命令reset,可以回退版本 reset命令有3种方式: 1.git   reset   –mixed:此为默认方式,不带任何参数的git r ...

  6. 321B

    MCMF必须是满足流量最大为前提下的最小费用流(这里是最大费用流) 因此还必须不断地枚举m的流量才行 #include<iostream> #include<algorithm> ...

  7. C语言变参函数的实现原理

    1. 变参函数简单示例 #include <stdarg.h> #include <stdio.h> int Accumlate(int nr, ...) { ; ; va_l ...

  8. Linux链接器脚本详解

    /* GNU linker script for STM32F405 */ /* Specify the memory areas */ MEMORY { FLASH (rx) : ORIGIN = ...

  9. python3 reversed() 函数笔记

    需要逆向循环序列的话,先正向定位序列,然后调用 reversed() 函数. for i in reversed(range(1, 10, 2)):        print(i) 97531

  10. Impala与Hive的优缺点和异同

    定位: HIVE:长时间的批处理查询分析 impala:实时交互式SQL查询 impala优缺点优点: 1. 生成执行计划树,不用多次启动job造成多余开销,并且减少中间结果数据写入磁盘,执行速度快 ...