最近在使用Quartz.net開發項目的時候,因爲業務需求,我需要在頁面進行任務的管理,比如暫停、恢復、顯示下次執行時間等。網上關於C#中的Quartz的信息很少,因此我很多是通過查找Java的API完成的,如果大家需要其它需求,也可以直接查找Java中Quartz怎麼使用的就行。
這裏我使用的是基於配置文件的方式進行的,對於Quartz.net不熟悉的可以看我的其它博客,這裏我就不多講了,直接開始上代碼。
我這裏建立一個Windows窗體應用,窗體類命名爲:MainView。
首先建立兩個Job類,其中的MainView mainView = MainView.GetInstance();mainView.JobList(); 先不用管,之後我會解釋爲什麼用這個,我這裏每10秒觸發一次Job,然後Job1執行至少需要15秒,爲了讓其不併發執行,加入了[DisallowConcurrentExecutionAttribute]屬性,我理解這東西類似於Java中的註解:
[DisallowConcurrentExecutionAttribute]
public class Job1:IJob
{
public void Execute(IJobExecutionContext context)
{
MainView mainView = MainView.GetInstance();
mainView.JobList();
//模擬運行該任務需要15秒
Thread.Sleep(15000);
MessageBox.Show("第一個Job:" + DateTime.Now.ToString());
}
}
public class Job2:IJob
{
public void Execute(IJobExecutionContext context)
{
MainView mainView = MainView.GetInstance();
mainView.JobList();
MessageBox.Show("第二個Job:" + DateTime.Now.ToString());
}
}
接着編寫Quartz的配置文件quartz_jobs.xml,每10秒運行一次:
<job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0">
<processing-directives>
<overwrite-existing-data>true</overwrite-existing-data>
</processing-directives>
<schedule>
<job>
<!--作業名字,隨便取-->
<name>Job1</name>
<!--組名,隨便取-->
<group>group1</group>
<!--描述-->
<description>job1</description>
<!--作業類,要定位到我們代碼中創建的作業,命名空間.類名-->
<job-type>QuartzTest.Job1, QuartzTest</job-type>
<durable>true</durable>
<recover>false</recover>
</job>
<trigger>
<!--cron表達式觸發器-->
<cron>
<!--觸發器名字,隨便取-->
<name>Trigger1</name>
<!--組名-->
<group>group1</group>
<!--描述-->
<description>trigger</description>
<!--作業名,觸發哪個作業-->
<job-name>Job1</job-name>
<!--作業的組名,與作業對應-->
<job-group>group1</job-group>
<misfire-instruction>DoNothing</misfire-instruction>
<cron-expression>0/10 * * * * ?</cron-expression>
</cron>
</trigger>
<job>
<!--作業名字,隨便取-->
<name>Job2</name>
<!--組名,隨便取-->
<group>group2</group>
<!--描述-->
<description>job2</description>
<!--作業類,要定位到我們代碼中創建的作業,命名空間.類名-->
<job-type>QuartzTest.Job2, QuartzTest</job-type>
<durable>true</durable>
<recover>false</recover>
</job>
<trigger>
<!--cron表達式觸發器-->
<cron>
<!--觸發器名字,隨便取-->
<name>Trigger2</name>
<!--組名-->
<group>group2</group>
<!--描述-->
<description>trigger</description>
<!--作業名,觸發哪個作業-->
<job-name>Job2</job-name>
<!--作業的組名,與作業對應-->
<job-group>group2</job-group>
<misfire-instruction>DoNothing</misfire-instruction>
<cron-expression>0/10 * * * * ?</cron-expression>
</cron>
</trigger>
</schedule>
</job-scheduling-data>
接着需要配置程序的配置文件,即App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="quartz" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</configSections>
<quartz>
<add key="quartz.scheduler.instanceName" value="ServerScheduler" />
<add key="quartz.threadPool.type" value="Quartz.Simpl.SimpleThreadPool, Quartz" />
<add key="quartz.threadPool.threadCount" value="10" />
<add key="quartz.threadPool.threadPriority" value="2" />
<add key="quartz.plugin.xml.type" value = "Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz" />
<add key="quartz.plugin.xml.fileNames" value = "quartz_jobs.xml" />
<add key="quartz.jobStore.misfireThreshold" value="60000" />
<add key="quartz.jobStore.type" value="Quartz.Simpl.RAMJobStore, Quartz" />
</quartz>
</configuration>
這裏爲了方便,我建立了一個QuartzManage類,裏面放了我們需要用到的一些屬性:
public class QuartzManage
{
public static string jobName = "";
public static string jobGroupName = "";
public static string triggerName = "";
public static string triggerGroupName = "";
//創建一個調度工廠
public static IScheduler scheduler = GetScheduler();
private static IScheduler GetScheduler()
{
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
IScheduler scheduler = stdSchedulerFactory.GetScheduler();
return scheduler;
}
}
界面設置如下:
這裏界面我加入了單例模式,待會解釋爲什麼使用單例模式,使用單例,記得在項目的Program.cs中將Application.Run(New MainView())修改爲Application.Run(MainView.GetInstance()):
private static MainView mainView = new MainView();
public static MainView GetInstance()
{
return mainView;
}
在啓動了項目後,應該要啓動這些定時任務,並且要將這些任務的信息給顯示出來,因此在主窗體加載的時候,需要開啓任務,並且將任務信息放入JobDataGridView中:
private void MainView_Load(object sender, EvventArgs e)
{
QuartzManage.scheduler.Start();
JobList();
}
這裏的JobList()是我單獨封裝的將信息顯示在JobDataGridView中的方法:
public void JobList()
{
//此方法獲取所有的Job的對象
IList<IJobExecutionContext> jobs = QuartzManage.scheduler.GetCurrentlyExecutingJobs();
//此方法獲取所有的Job的group的名字
IList<string> name = QuartzManage.scheduler.GetJobGroupNames();
for(int i = 0;i < name.Count;i++)
{
//給jobDataGridView的行賦值,表明多少行
this.jobDataGridView.RowCount = name.Count;
//GetJobKeys獲取到所有Job的JobKey,相當於Job信息
foreach(JobKey jobKey in QuartzManage.scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(name[i])))
{
this.jobDataGridView.Rows[i].Cells[0].Value = jobKey.Name;
this.jobDataGridView.Rows[i].Cells[1].Value = jobKey.Group;
}
//GetTriggerKeys獲取到所有Trigger的TriggerKey,相當於Trigger信息
foreach(TriggerKey triggerKey in QuartzManage.scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.GroupEquals(name[i])))
{
//獲取觸發器的狀態
this.jobDataGridView.Rows[i].Cells[2].Value = QuartzManage.shceduler.GetTriggerState(triggerKey);
this.jobDataGridView.Rows[i].Cells[3].Value = triggerKey.Name;
this.jobDataGridView.Rows[i].Cells[4].Value = trigger.Group;
//獲取下一次觸發時間
ICronTrigger cronTrigger = (ICronTrigger)QuartzManage.scheduler.GetTrigger(triggerKey);
CronExpression cronExpression = new CronExpression(cronTrigger.CronExpressionString);
this.jobDataGridView.Rows[i].Cells[5].Value = cronExpression.GetNextValidTimeAfter(DateTime.Now).Value.ToLocalTime();
}
}
}
接着就是選中jobDataGridView中的一個任務,這裏爲了讓點擊一個單元格就選中整行,添加了一些代碼,這裏不做細講,可以去查看我的博客中的第19個例子:https://blog.csdn.net/qq_41061437/article/details/99940094
給jobDataGridView添加CellClick事件:
private void JobDataGridView_CellClick(object sender, DataGridViewCellEventArgs e)
{
QuartzManage.jobName = (string)jobDataGridView.SelectedRows[0].Cells["Jobs"].Value;
QuartzManage.jobGroupName = (string)jobDataGridView.SelectedRows[0].Cells["JobGroups"].Value;
QuartzManage.triggerName = (string)jobDataGridView.SelectedRows[0].Cells["Triggers"].Value;
QuartzManage.triggerGroupName = (string)jobDataGridView.SelectedRows[0].Cells["TriggerGroupName"].Value;
}
接着就是選中之後點擊暫停按鈕,讓任務暫停了:
private void StopButton_Click(object sender, EventArgs e)
{
TriggerKey triggerKey = new TriggerKey(QuartzManage.triggerName, QuartzManage.triggerGroupName);
if(QuartzManage.jobName.Equals(""))
{
MessageBox.Show("請選擇要暫停的任務!");
}
//判斷任務狀態
else if(QuartzManage.scheduler.GetTriggerState(triggerKey).ToString() == "Paused")
{
MessageBox.Show("該任務已暫停");
}
else
{
QuartzManage.scheduler.PauseTrigger(triggerKey);
JobList();
MessageBox.Show("任務" + QuartzManage.jobName + "已暫停!");
}
}
接着就是恢復任務:
private void ResumeButton_Click(object sender, EventArgs e)
{
TriggerKey triggerKey = new TriggerKey(QuartzManage.triggerName, QuartzManage.triggerGroupName);
if(QuartzManage.jobName.Equals(""))
{
MessageBox.Show("請先選擇要恢復的任務!");
}
else if(QuartzManage.scheduler.GetTriggerState(triggerKey).ToString() == "Normal")
{
MessageBox.Show("任務正在運行,無需恢復");
}
else
{
QuartzManage.scheduler.ResumeTrigger(triggerKey);
JobList();
MessageBox.Show("任務" + QuartzManage.jobName + "已恢復!");
}
}
整個MainView.cs類整合就是:
public partial class MainView : Form
{
private static MainView mainView = new MainView();
public static MainView GetInstance()
{
return mainView;
}
public MainView()
{
InitializeComponent();
}
/// <summary>
/// 窗體加載事件
/// <summary>
private void MainView_Load(object sender, EventArgs e)
{
QuartzManage.scheduler.Start();
JobList();
}
/// <summary>
/// table展示
/// <summary>
public void JobList()
{
//此方法獲取所有的Job的對象
IList<IJobExecutionContext> jobs = QuartzManage.scheduler.GetCurrentlyExecutingJobs();
//此方法獲取所有的Job的group的名字
IList<string> name = QuartzManage.scheduler.GetJobGroupNames();
for (int i = 0; i < name.Count; i++)
{
//給jobDataGridView的行賦值,表明多少行
this.jobDataGridView.RowCount = name.Count;
//GetJobKeys獲取到所有Job的JobKey,相當於Job信息
foreach (JobKey jobKey in QuartzManage.scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(name[i])))
{
this.jobDataGridView.Rows[i].Cells[0].Value = jobKey.Name;
this.jobDataGridView.Rows[i].Cells[1].Value = jobKey.Group;
}
//GetTriggerKeys獲取到所有Trigger的TriggerKey,相當於Trigger信息
foreach (TriggerKey triggerKey in QuartzManage.scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.GroupEquals(name[i])))
{
//獲取觸發器的狀態
this.jobDataGridView.Rows[i].Cells[2].Value = QuartzManage.scheduler.GetTriggerState(triggerKey);
this.jobDataGridView.Rows[i].Cells[3].Value = triggerKey.Name;
this.jobDataGridView.Rows[i].Cells[4].Value = triggerKey.Group;
//獲取下一次觸發時間
ICronTrigger cronTrigger = (ICronTrigger)QuartzManage.scheduler.GetTrigger(triggerKey);
CronExpression cronExpression = new CronExpression(cronTrigger.CronExpressionString);
this.jobDataGridView.Rows[i].Cells[5].Value = cronExpression.GetNextValidTimeAfter(DateTime.Now).Value.ToLocalTime();
}
}
}
/// <summary>
/// 單擊CellClick事件
/// <summary>
private void JobDataGridView_CellClick(object sender, DataGridViewCellEventArgs e)
{
QuartzManage.jobName = (string)jobDataGridView.SelectedRows[0].Cells["Jobs"].Value;
QuartzManage.jobGroupName = (string)jobDataGridView.SelectedRows[0].Cells["JobGroups"].Value;
QuartzManage.triggerName = (string)jobDataGridView.SelectedRows[0].Cells["Triggers"].Value;
QuartzManage.triggerGroupName = (string)jobDataGridView.SelectedRows[0].Cells["TriggerGroupName"].Value;
}
/// <summary>
/// 暫停任務
/// <summary>
private void StopButton_Click(object sender, EventArgs e)
{
TriggerKey triggerKey = new TriggerKey(QuartzManage.triggerName, QuartzManage.triggerGroupName);
if (QuartzManage.jobName.Equals(""))
{
MessageBox.Show("請選擇要暫停的任務!");
}
//判斷任務狀態
else if (QuartzManage.scheduler.GetTriggerState(triggerKey).ToString() == "Paused")
{
MessageBox.Show("該任務已暫停");
}
else
{
QuartzManage.scheduler.PauseTrigger(triggerKey);
JobList();
MessageBox.Show("任務" + QuartzManage.jobName + "已暫停!");
}
}
/// <summary>
/// 恢復任務
/// <summary>
private void ResumeButton_Click(object sender, EventArgs e)
{
TriggerKey triggerKey = new TriggerKey(QuartzManage.triggerName, QuartzManage.triggerGroupName);
if (QuartzManage.jobName.Equals(""))
{
MessageBox.Show("請先選擇要恢復的任務!");
}
else if (QuartzManage.scheduler.GetTriggerState(triggerKey).ToString() == "Normal")
{
MessageBox.Show("任務正在運行,無需恢復");
}
else
{
QuartzManage.scheduler.ResumeTrigger(triggerKey);
JobList();
MessageBox.Show("任務" + QuartzManage.jobName + "已恢復!");
}
}
private void MainView_FormClosing(object sender, FormClosingEventArgs e)
{
DialogResult result = MessageBox.Show("是否關閉窗口?", "關閉", MessageBoxButtons.YesNo, MessageBoxIcon.None);
if (result == DialogResult.No)
{
e.Cancel = true;
}
else
{
e.Cancel = false;
QuartzManage.scheduler.Shutdown();
}
}
}
啓動項目,效果如圖:
下面我們來解釋一下其中有些地方,比如Job類中,我使用了MainView mainView = MainView.GetInstance();mainView.JobList();這個的作用是爲了更新顯示的列表,主要是更新下次觸發時間,當Job觸發之後,那麼我們應該修改其的下次觸發時間,這裏正常來說用個觀察者模式來進行更適合一點,但我這裏這樣用比較簡單,也比較方便,因此這樣使用。這樣應該就能夠理解爲什麼要使用單例模式了,因爲我要在其它類裏面調用MainView裏面的方法,而我只能給一個窗口顯示,因此使用的單例模式。
這裏觸發器都是每10秒觸發一次,然後在Job1中,休眠了15秒。也就是說Job1至少需要運行15秒,爲了能達到上一個任務運行完再執行下一個任務的效果,這裏給Job1加入了[DisallowConcurrentExecutionAttribute]的屬性。
當一個任務因爲暫停錯過觸發時間,當再次恢復任務的時候,我們這裏直接忽略它,等待下一次觸發時間,爲了達到此效果,在配置文件中對misfire屬性進行了配置:<misfire-instruction>DoNothing</misfire-instruction>,關於其的配置,我們在其的xsd文件中能夠清晰的看到幾種不同的觸發其含有的不同觸發機制。
當然,在Quartz中還有很多不錯的方法,具體的可以直接點擊進源碼中進行查看,之後又機會,我會對裏面的方法及其使用再進行深入或者簡單的介紹。
我已經把這個小demo上傳到CSDN,資源下載鏈接:https://download.csdn.net/download/qq_41061437/11785180