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’ */
運行效果圖: