C#作业调度Quartz.NET学习笔记

一、简单介绍

Quartz.NET是一个强大、开源、轻量的作业调度框架,是 OpenSymphony 的 Quartz API 的.NET移植,用C#改写,可用于WinForm和ASP.NET应用中。它灵活而不复杂,可以为执行一个作业而创建简单或复杂的作业调度。它有很多特征,如:数据库支持、集群、插件、支持cron-like表达式等等。

官网:http://www.quartz-scheduler.net/

源码:https://github.com/quartznet/quartznet

示例:http://www.quartz-scheduler.net/documentation/quartz-2.x/quick-start.html

二、概念解释

Scheduler:作业调度器。

IJob:作业接口,继承并实现Execute, 编写执行的具体作业逻辑。

JobBuilder:根据设置,生成一个详细作业信息(JobDetail)。

TriggerBuilder:根据规则,生产对应的Trigger。

三、示例程序

3.1、界面

新建一个WinForm程序Client,项目右键->属性->应用程序->输出类型,选择控制台应用程序。

3.2、引用

项目右键->管理 NuGet 程序包->Quartz.NET。

3.2、作业

新建一个类DataSyncJob并继承IJob,代表它是一个作业,同时实现Execute方法。

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Quartz;namespace LinkTo.Test.Quartz.Client{    //打上DisallowConcurrentExecution标签让Job进行单线程跑,避免没跑完时的重复执行。    [DisallowConcurrentExecution]    public class DataSyncJob : IJob    {        public Task Execute(IJobExecutionContext context)        {            JobDataMap keyValuePairs = context.MergedJobDataMap;            if (keyValuePairs.Count > 0 && keyValuePairs.Contains("Hello"))            {                string value = context.MergedJobDataMap.Get("Hello").ToString();                return Task.Run(() =>                {                    Console.WriteLine(DateTime.Now + $" Hello:{value}" + Environment.NewLine);                });            }            else            {                return Task.Run(() =>                {                    Console.WriteLine(DateTime.Now + Environment.NewLine);                });            }        }    }}

View Code

说明:

1)一般来说,作业需打上[DisallowConcurrentExecution]标签,以避免当次作业尚未完成时又被开始调度执行。

2)作业可以接收触发器传递过来的参数(Key-Value),上面作业接收的是"Hello"参数。

3.3、调度

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;using Quartz;using Quartz.Impl;namespace LinkTo.Test.Quartz.Client{    public partial class Main : Form    {        //调度器工厂        private ISchedulerFactory factory;        //调度器        private IScheduler scheduler;        public Main()        {            InitializeComponent();            //按钮状态            btnStart.Enabled = true;            btnStop.Enabled = false;        }        /// <summary>        /// 开始        /// </summary>        /// <param name="sender"></param>        /// <param name="e"></param>        private async void btnStart_Click(object sender, EventArgs e)        {            //1、创建一个调度器            factory = new StdSchedulerFactory();            scheduler = await factory.GetScheduler();            await scheduler.Start();            //2、创建一个任务            IJobDetail job = JobBuilder.Create<DataSyncJob>().WithIdentity("DataSync", "DataSync_Group").Build();            //3、创建一个触发器(有4种触发器供选择)            //4、将任务与触发器添加到调度器中            #region 触发器1:WithSimpleSchedule            //ITrigger simpleTrigger = TriggerBuilder.Create()            //    .WithIdentity("DataSync_SimpleTrigger", "DataSync_TriggerGroup")            //    .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever())            //    .Build();            //await scheduler.ScheduleJob(job, simpleTrigger);            #endregion            #region 触发器2:WithDailyTimeIntervalSchedule            //ITrigger dailyTimeTrigger = TriggerBuilder.Create()            //    .WithIdentity("DataSync_DailyTimeTrigger", "DataSync_TriggerGroup")            //    .WithDailyTimeIntervalSchedule(x => x.OnEveryDay().WithIntervalInSeconds(5))            //    .Build();            //await scheduler.ScheduleJob(job, dailyTimeTrigger);            #endregion            #region 触发器3:WithCalendarIntervalSchedule            //ITrigger calendarTrigger = TriggerBuilder.Create()            //    .WithIdentity("DataSync_CalendarTrigger", "DataSync_TriggerGroup")            //    .WithCalendarIntervalSchedule(x => x.WithIntervalInSeconds(5))            //    .Build();            //await scheduler.ScheduleJob(job, calendarTrigger);            #endregion            #region 触发器4:WithCronSchedule(带传递参数"Hello")            ITrigger cronTrigger = TriggerBuilder.Create()                .WithIdentity("DataSync_CronTrigger", "DataSync_TriggerGroup")                .WithCronSchedule("0/5 * * * * ?")                .UsingJobData("Hello", Guid.NewGuid().ToString())                .Build();            await scheduler.ScheduleJob(job, cronTrigger);            #endregion            //5、开始执行            await scheduler.Start();            //按钮状态            btnStart.Enabled = false;            btnStop.Enabled = true;        }        /// <summary>        /// 停止        /// </summary>        /// <param name="sender"></param>        /// <param name="e"></param>        private void btnStop_Click(object sender, EventArgs e)        {            if (scheduler != null)            {                scheduler.Shutdown(true);            }            //按钮状态            btnStart.Enabled = true;            btnStop.Enabled = false;        }    }}

View Code

说明:

1)Job及Trigger在WithIdentity时都会用到**Group,此处Group的作用是用于分类,相当于一个命名空间。

2)触发器有4种,分别是WithSimpleSchedule、WithDailyTimeIntervalSchedule、WithCalendarIntervalSchedule、WithCronSchedule,常用第1种及第4种。

3)WithCronSchedule触发器使用的是Cron表达式。

3.4、结果

四、Cron表达式

1)在线Cron表达式生成器:https://cron.qqe2.com/

2)官方英文介绍:https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/crontrigger.html

3)Cron表达式整体上还是比较容易理解的,只有一点需要注意"?"号的用法。"?"可以用在 Day of Month 和 Day of Week 中,比如:每月1号的每小时的第31分钟,正确的表达式是:* 31 * 1 * ?,而不能是:* 31 * 1 * *,因为这样代表的是每周的任意一天。

Cron表达式由7段构成:秒 分 时 日 月 星期 年(可选)"-":表示范围,MON-WED表示星期一到星期三。",":表示列举,MON,WEB表示星期一和星期三。"*":表示"每",每天、每月、每周、每年等。"/":表示增量,0/15(分钟)表示每15分钟,在0分以后开始;3/20表示每20分钟,从3分钟以后开始。"?":只能出现在日、星期段里面,表示不指定具体的值。"L":只能出现在日、星期段里面,是Last的缩写,表示如一个月的最后一天、一个星期的最后一天(星期六)。"W":表示工作日,距离给定值最近的工作日。"#":表示一个月的第几个星期几,"6#3"表示每个月的第三个星期五(1=SUN...6=FRI,7=SAT)。

4)官方示例:

表达式 解释
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期间的每1分钟触发
0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
0 10,44 14 ? 3 WED 每年三月的星期三的下午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 L-2 * ? 每个月的第二天到最后一天的上午10:15触发
0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
0 15 10 ? * 6L 每个月最后一个星期五上午10时15分触发
0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发
0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
0 0 12 1/5 * ? 每月每隔5天下午12点(中午)触发, 从每月的第一天开始
0 11 11 11 11 ? 每11月11日上午11时11分触发

五、一行代码实现调度

新建一个TestJob:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Quartz;namespace LinkTo.Test.Quartz.Client{    //打上DisallowConcurrentExecution标签让Job进行单线程跑,避免没跑完时的重复执行。    [DisallowConcurrentExecution]    public class TestJob : IJob    {        public Task Execute(IJobExecutionContext context)        {            return Task.Run(() =>            {                Console.WriteLine(DateTime.Now + Environment.NewLine);            });        }    }}

TestJob.cs

新建一个调度封装类QuartzFactory:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Quartz;using Quartz.Impl;using Quartz.Impl.Triggers;namespace LinkTo.Test.Quartz.Client{    public class QuartzFactory    {        //调度器工厂        private static ISchedulerFactory factory = null;        //调度器        private static IScheduler scheduler = null;        /// <summary>        /// 构造函数        /// </summary>        static QuartzFactory()        {            factory = new StdSchedulerFactory();            scheduler = factory.GetScheduler().Result;            scheduler.Start();        }        #region 触发器1:添加Job        /// <summary>        /// 触发器1:添加Job并以周期的形式运行        /// </summary>        /// <typeparam name="T"></typeparam>        /// <param name="jobName"></param>        /// <param name="startTime"></param>        /// <param name="simpleTime"></param>        /// <param name="jobDataMap"></param>        /// <returns></returns>        public static DateTimeOffset AddJob<T>(string jobName, DateTimeOffset startTime, TimeSpan simpleTime, Dictionary<string, object> jobDataMap) where T : IJob        {            IJobDetail jobCheck = JobBuilder.Create<T>().WithIdentity(jobName, jobName + "_Group").Build();            jobCheck.JobDataMap.PutAll(jobDataMap);            ISimpleTrigger triggerCheck = new SimpleTriggerImpl(jobName + "_SimpleTrigger",                 jobName + "_TriggerGroup",                startTime,                null,                SimpleTriggerImpl.RepeatIndefinitely,                simpleTime);            return scheduler.ScheduleJob(jobCheck, triggerCheck).Result;        }        /// <summary>        /// 触发器1:添加Job并以周期的形式运行        /// </summary>        /// <typeparam name="T"></typeparam>        /// <param name="jobName"></param>        /// <param name="startTime"></param>        /// <param name="simpleTime">毫秒数</param>        /// <param name="mapKey"></param>        /// <param name="mapValue"></param>        /// <returns></returns>        public static DateTimeOffset AddJob<T>(string jobName, DateTimeOffset startTime, int simpleTime, string mapKey, object mapValue) where T : IJob        {            Dictionary<string, object> jobDataMap = new Dictionary<string, object>            {                { mapKey, mapValue }            };            return AddJob<T>(jobName, startTime, TimeSpan.FromMilliseconds(simpleTime), jobDataMap);        }        /// <summary>        /// 触发器1:添加Job并以周期的形式运行        /// </summary>        /// <typeparam name="T"></typeparam>        /// <param name="jobName"></param>        /// <param name="startTime"></param>        /// <param name="simpleTime"></param>        /// <returns></returns>        public static DateTimeOffset AddJob<T>(string jobName, DateTimeOffset startTime, TimeSpan simpleTime) where T : IJob        {            return AddJob<T>(jobName, startTime, simpleTime, new Dictionary<string, object>());        }        /// <summary>        /// 触发器1:添加Job并以周期的形式运行        /// </summary>        /// <typeparam name="T"></typeparam>        /// <param name="jobName"></param>        /// <param name="startTime"></param>        /// <param name="simpleTime">毫秒数</param>        /// <returns></returns>        public static DateTimeOffset AddJob<T>(string jobName, DateTimeOffset startTime, int simpleTime) where T : IJob        {            return AddJob<T>(jobName, startTime, TimeSpan.FromMilliseconds(simpleTime));        }        /// <summary>        /// 触发器1:添加Job并以周期的形式运行        /// </summary>        /// <typeparam name="T"></typeparam>        /// <param name="jobName"></param>        /// <param name="simpleTime">毫秒数</param>        /// <returns></returns>        public static DateTimeOffset AddJob<T>(string jobName, int simpleTime) where T : IJob        {            return AddJob<T>(jobName, DateTime.UtcNow.AddMilliseconds(1), TimeSpan.FromMilliseconds(simpleTime));        }        #endregion        #region 触发器4:添加Job        /// <summary>        /// 触发器4:添加Job并以定点的形式运行        /// </summary>        /// <typeparam name="T"></typeparam>        /// <param name="jobName"></param>        /// <param name="cronTime"></param>        /// <param name="jobDataMap"></param>        /// <returns></returns>        public static DateTimeOffset AddJob<T>(string jobName, string cronTime, string jobData) where T : IJob        {            IJobDetail jobCheck = JobBuilder.Create<T>().WithIdentity(jobName, jobName + "_Group").UsingJobData("jobData", jobData).Build();            ICronTrigger cronTrigger = new CronTriggerImpl(jobName + "_CronTrigger", jobName + "_TriggerGroup", cronTime);            return scheduler.ScheduleJob(jobCheck, cronTrigger).Result;        }        /// <summary>        /// 触发器4:添加Job并以定点的形式运行        /// </summary>        /// <typeparam name="T"></typeparam>        /// <param name="jobName"></param>        /// <param name="cronTime"></param>        /// <returns></returns>        public static DateTimeOffset AddJob<T>(string jobName, string cronTime) where T : IJob        {            return AddJob<T>(jobName, cronTime, null);        }        #endregion        /// <summary>        /// 修改触发器时间重载        /// </summary>        /// <param name="jobName">Job名称</param>        /// <param name="timeSpan">TimeSpan</param>        /// </summary>        public static void UpdateTime(string jobName, TimeSpan simpleTimeSpan)        {            TriggerKey triggerKey = new TriggerKey(jobName + "_SimpleTrigger", jobName + "_TriggerGroup");            SimpleTriggerImpl simpleTriggerImpl = scheduler.GetTrigger(triggerKey).Result as SimpleTriggerImpl;            simpleTriggerImpl.RepeatInterval = simpleTimeSpan;            scheduler.RescheduleJob(triggerKey, simpleTriggerImpl);        }        /// <summary>        /// 修改触发器时间重载        /// </summary>        /// <param name="jobName">Job名称</param>        /// <param name="simpleTime">分钟数</param>        /// <summary>        public static void UpdateTime(string jobName, int simpleTime)        {            UpdateTime(jobName, TimeSpan.FromMinutes(simpleTime));        }        /// <summary>        /// 修改触发器时间重载        /// </summary>        /// <param name="jobName">Job名称</param>        /// <param name="cronTime">Cron表达式</param>        public static void UpdateTime(string jobName, string cronTime)        {            TriggerKey triggerKey = new TriggerKey(jobName + "_CronTrigger", jobName + "_TriggerGroup");            CronTriggerImpl cronTriggerImpl = scheduler.GetTrigger(triggerKey).Result as CronTriggerImpl;            cronTriggerImpl.CronExpression = new CronExpression(cronTime);            scheduler.RescheduleJob(triggerKey, cronTriggerImpl);        }        /// <summary>        /// 暂停所有Job        /// </summary>        public static void PauseAll()        {            scheduler.PauseAll();        }        /// <summary>        /// 恢复所有Job        /// </summary>        public static void ResumeAll()        {            scheduler.ResumeAll();        }        /// <summary>        /// 删除指定Job        /// </summary>        /// <param name="jobName"></param>        public static void DeleteJob(string jobName)        {            JobKey jobKey = new JobKey(jobName, jobName + "_Group");            scheduler.DeleteJob(jobKey);        }        /// <summary>        /// 卸载定时器        /// </summary>        /// <param name="isWaitForToComplete">是否等待Job执行完成</param>        public static void Shutdown(bool isWaitForToComplete)        {            scheduler.Shutdown(isWaitForToComplete);        }    }}

QuartzFactory.cs

一行代码实现作业调度按钮代码:

/// <summary>        /// 一行代码实现作业调度        /// </summary>        /// <param name="sender"></param>        /// <param name="e"></param>        private void btnOneCode_Click(object sender, EventArgs e)        {            if (btnOneCode.Text == "一行代码实现作业调度")            {                string cronTime = "0/5 * * * * ?";                QuartzFactory.AddJob<TestJob>("TestJob", cronTime);                btnOneCode.Text = "Stop";            }            else            {                QuartzFactory.DeleteJob("TestJob");                btnOneCode.Text = "一行代码实现作业调度";            }        }

btnOneCode_Click

参考自:

https://www.cnblogs.com/best/p/7658573.html

(0)

相关推荐

  • springboot Quartz 定时任务工具类

    package org.fh.util; import java.util.Map; import org.quartz.CronScheduleBuilder; import org.quartz. ...

  • C#作业调度FluentScheduler学习笔记

    一.简介 FluentScheduler是一个简单的任务调度框架,定时任务管理器. GitHub地址:https://github.com/fluentscheduler/FluentSchedule ...

  • 一则公报案例学习笔记:对修改股东出资期限应否适用资本多数决规则的思考|审判研究

    一.问题的提出 2021年第3期<最高人民法院公报案例>刊登了鸿大(上海)投资管理有限公司与姚锦城公司决议纠纷上诉案,裁判要旨为:"公司股东滥用控股地位,以多数决方式通过修改出资 ...

  • JAVA多线程学习笔记整理

    多线程: 三种创建方法 继承Thread类,以线程运行内容重写run方法,创建Thread对象并用start方法启动该线程. (匿名内部类) (Lambda表达式) 实现Runable接口,以线程运行 ...

  • 周哥学习笔记(2021.5.8)

    心理界限存在的意义,正是为了帮助人们控制情绪进入的量,不至于太过冷漠或太过投入,让我们保持一个合适的距离与外界互动. 人没有办法只通过吸收变得更美好和丰富,它必须通过大胆的碰撞和创造.如果不能保持足够 ...

  • 【学习笔记】控制角色移动的N种方法,但都离不开重复执行

    [学习笔记]控制角色移动的N种方法,但都离不开重复执行 今天我们讲一下控制角色移动的多种方法,因为缺少操作实例,希望课下同学们结合例子好好练习. 首先,我们说一下控制角色移动的多种方法.最比较常见的就 ...

  • 胡希恕伤寒论学习笔记——42

    42.太阳病,外证未解,脉浮弱者,当以汗解,宜桂枝汤. 字面意思是说:太阳病,外证依然存在,脉是浮弱的,治疗上依然需要通过出汗的方法,这时应该用桂枝汤一类的方剂. "宜"字说明不是 ...

  • 量柱擒涨停 - 量柱战法学习笔记(2)

    四.倍量战术 1.倍量的理解 [形态特征]:与前一个交易日比较高出1倍或1倍以上,就是倍量(4倍以上为发烧柱) ; [本质特征]:体现主力强势态度,主动(倍量阳/阴)买/卖盘吸筹坚决; [位置性质]: ...

  • 胡希恕伤寒论学习笔记——43

    43.太阳病,下之微喘者,表未解故也,桂枝加厚朴杏子汤主之. 桂枝加厚朴杏子汤方 桂枝三两 芍药三两 厚朴二两(炙,去皮) 杏仁五十枚(去皮尖)甘草二两(炙) 生姜三两(切)大枣十二枚(掰) 上七味, ...

  • 学习笔记:信息技术

    学习笔记:信息技术