一個普通的 Job 實現如下:
public class Job1 : IJob { public void Execute(IJobExecutionContext context) { Console.WriteLine(DateTime.Now + ": Job1" + m); } } public class Program { static void Main(string[] args) { var props = new NameValueCollection(); //使用簡單線程池 props["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz"; //最大線程數 props["quartz.threadPool.threadCount"] = "10"; //線程優先級:正常 props["quartz.threadPool.threadPriority"] = "Normal"; //初始化調度器 IScheduler scheduler = new StdSchedulerFactory(props).GetScheduler(); //Cron 觸發器,每隔 1 秒觸發一次 ITrigger trig = TriggerBuilder.Create().WithCronSchedule("0/1 * * * * ?").Build(); //將作業 Job1 加入調度計劃中 scheduler.ScheduleJob(JobBuilder.Create<Job1>().Build(), trig); //開始執行 scheduler.Start(); Console.ReadLine(); scheduler.Shutdown(); } }
執行結果如下:可以看到,Job1 准確的每隔 1 秒執行一次
現在問題來了:如果 Job1 中的操作執行時間很長,超過了間隔時間 1 秒,會發生什麼情況?代碼如下:
public class Job1 : IJob { public void Execute(IJobExecutionContext context) { Console.WriteLine(DateTime.Now + ": Job1" + m); //等待 5 秒 Thread.Sleep(5000); } }
執行結果如下:
我們會發現,Quartz 仍然會按照我們設定的每隔 1 秒觸發一次。
這是因為默認情況下,當Job執行時間超過間隔時間時,調度框架為了能讓任務按照我們預定的時間間隔執行,會馬上啟用新的線程執行任務。
若我們希望當前任務執行完之後再執行下一輪任務,也就是不要並發執行任務,該如何解決呢?
第一種方法:設置 quartz.threadPool.threadCount 最大線程數為 1。這樣到了第二次執行任務時,若當前還沒執行完,調度器想新開一個線程執行任務,但我們卻設置了最大線程數為 1 個(即:沒有足夠的線程提供給調度器),則調度器會等待當前任務執行完之後,再立即調度執行下一次任務。(注意:此方法僅適用於Quartz中僅有一個Job,如果有多個Job,會影響其他Job的執行)
第二種方法:在 Job 類的頭部加上 [DisallowConcurrentExecution],表示禁用並發執行。(推薦使用此方法)
//不允許此 Job 並發執行任務(禁止新開線程執行) [DisallowConcurrentExecution] public class Job1 : IJob { }