Quartz是一个完全由JAVA编写的开源作业调度框架。
Quartz.NET是Quartz的.NET移植,它用C#写成,可用于.Net以及.Net Core的应用中。
目前最新的quartz.net版本3.0.6 只支持.netframework4.5.2及.netstandard2.0及以上版本
官方实例:https://www.quartz-scheduler.net/documentation/quartz-3.x/quick-start.html
using Quartz; using Quartz.Impl; using Quartz.Logging; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Demo { class Program { private static void Main(string[] args) { LogProvider.SetCurrentLogProvider(new ConsoleLogProvider()); RunProgramRunExample().GetAwaiter().GetResult(); Console.WriteLine("Press any key to close the application"); Console.ReadKey(); } private static async Task RunProgramRunExample() { try { // Grab the Scheduler instance from the Factory NameValueCollection props = new NameValueCollection { { "quartz.serializer.type", "binary" } }; StdSchedulerFactory factory = new StdSchedulerFactory(props); IScheduler scheduler = await factory.GetScheduler(); // and start it off await scheduler.Start(); // define the job and tie it to our HelloJob class IJobDetail job = JobBuilder.Create<HelloJob>() .WithIdentity("job1", "group1") .Build(); // Trigger the job to run now, and then repeat every 10 seconds ITrigger trigger = TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartNow() .WithSimpleSchedule(x => x .WithIntervalInSeconds(10) .RepeatForever()) .Build(); // Tell quartz to schedule the job using our trigger await scheduler.ScheduleJob(job, trigger); // some sleep to show what's happening await Task.Delay(TimeSpan.FromSeconds(60)); // and last shut down the scheduler when you are ready to close your program await scheduler.Shutdown(); } catch (SchedulerException se) { Console.WriteLine(se); } } // simple log provider to get something to the console private class ConsoleLogProvider : ILogProvider { public Logger GetLogger(string name) { Logger loger = LoggerMethod; return loger; return (level, func, exception, parameters) => { if (level >= LogLevel.Info && func != null) { Console.WriteLine("[" + DateTime.Now.ToLongTimeString() + "] [" + level + "] " + func(), parameters); } return true; }; } private bool LoggerMethod(LogLevel logLevel, Func<string> messageFunc, Exception exception = null, params object[] formatParameters) { if (logLevel >= LogLevel.Info && messageFunc != null) { Console.WriteLine("[" + DateTime.Now.ToLongTimeString() + "] [" + logLevel + "] " + messageFunc(), formatParameters); } return true; } public IDisposable OpenNestedContext(string message) { throw new NotImplementedException(); } public IDisposable OpenMappedContext(string key, string value) { throw new NotImplementedException(); } } } public class HelloJob : IJob { public async Task Execute(IJobExecutionContext context) { await Console.Out.WriteLineAsync("Greetings from HelloJob!"); } } }
看了这个官方的示例,你会发现QuarztNet3.0版本较之2.0版本,引入了async/await
下面记录一下学习过程:
一、使用VS2013新建Winform项目,.Net版本为4.5.2,通过Nuget命令行获取Quarzt.Net:Install-Package Quartz. 如果你在安装过程中报错,那么,要注意你的.Net版本
二、万事俱备,开始编码
多任务,一个每分钟的第30秒播放音频,一个每分钟的第0秒写文本文件
private async void PlaySound() { //1.通过工厂获取一个调度器的实例 StdSchedulerFactory factory = new StdSchedulerFactory(); _scheduler = await factory.GetScheduler(); await _scheduler.Start(); //创建任务对象 IJobDetail job = JobBuilder.Create<SoundJob>() .WithIdentity("job1", "group1") .Build(); //创建触发器 ITrigger trigger = TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartNow() .WithCronSchedule("30 0/1 * * * ?")//每分钟的第30秒执行 .Build(); //将任务加入到任务池 await _scheduler.ScheduleJob(job, trigger); job = JobBuilder.Create<PrintJob>() .WithIdentity("job2", "group1") .Build(); trigger = TriggerBuilder.Create() .WithIdentity("trigger2", "group1") .StartNow() .WithCronSchedule("0 0/1 * * * ?")//每分钟的第0秒执行 .Build(); await _scheduler.ScheduleJob(job, trigger); }
public class PrintJob : IJob { string fileName = "printlog.txt"; public Task Execute(IJobExecutionContext context) { StreamWriter writer = new StreamWriter(fileName, true); Task task = writer.WriteLineAsync(string.Format("{0}", DateTime.Now.ToLongTimeString())); writer.Close(); writer.Dispose(); return task; } }
public class SoundJob : IJob { public static Action<string> _printLogCallBack; public string Sound { get; set; } public Task Execute(IJobExecutionContext context) { JobDataMap jobDataMap = context.JobDetail.JobDataMap; string sound = jobDataMap.GetString("sound"); int number = jobDataMap.GetInt("number"); if (_printLogCallBack != null) { _printLogCallBack(string.Format("{0}[{1}] 执行任务 Sound {2}", Environment.NewLine, DateTime.Now.ToLongTimeString(), Sound)); } return Task.Factory.StartNew(() => { SoundPlayer player = new SoundPlayer(); player.SoundLocation = @"F:\FFOutput\421204264234974-.wav"; player.Load(); player.Play(); }); } }
以上是主要部分,另外还涉及到日志部分,日志我是直接输出到UI上,我们可以看到以下编码的区别,ConsoleLogProvider中输出的日志可以直接打印,而SoundJob中的却不可以,说明SoundJob中的方法是异步执行的,需要解决跨线程访问UI控件的问题,我们可以使用UI线程的上下文对象SynchronizationContext _syncContext,将输出日志的方法,委托给UI线程执行。
private void Form1_Load(object sender, EventArgs e) { ConsoleLogProvider logProvider = new ConsoleLogProvider(); logProvider.SetLogCallBack((log) => { this.rchMessage.AppendText(log); }); SoundJob._printLogCallBack = (log) => { _syncContext.Send((obj) => { this.rchMessage.AppendText(log.ToString()); }, null); }; LogProvider.SetCurrentLogProvider(logProvider); }
关于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)
如果Minutes的数值是 '0/15' ,表示从0开始每15分钟执行
如果Minutes的数值是 '3/20' ,表示从3开始每20分钟执行,也就是‘3/23/43’
*/
运行效果图: