Quartz.net is a very powerful job scheduling framework, suitable for a variety of scheduled execution of business processing, similar to the Windows comes with the Task Scheduler, where the use of cron expressions to implement a variety of timing trigger conditions I think the most surprising place.
Quartz.net mainly uses the following classes:
IScheduler--Scheduler
Ijobdetail--Job tasks
Itrigger--Trigger
If we use a timer to write a similar scheduled execution of the task program, we should have: (The following is the idea, the purpose is to let everyone figure out the relationship between the three interface above quartz.net)
Scheduletimer--timer, executed once per second;
Triggerclass--to determine whether the job task needs to be performed, scheduletimer each execution, the new thread should call Triggerclass member Needexecute method or property;
jobclass--the specific Job task class, Triggerclass, if Triggerclass.needexecute returns True, then the Jobclass member Execute method should be executed;
Well, the introduction of Quartz.net is very much, I am not here to say, the following will mainly introduce how to implement pseudo-AOP write log function.
AOP does not know, please click here to understand.
Although Quartz.net has integrated the log4net's write log function, just in config can be configured, but I think the framework is written in the log does not meet my requirements, I need to follow the actual business needs in some conditions to write log, so only this article.
The following is the implementation of a job package class, can also be seen as the job proxy class, the complete code is as follows:
[Disallowconcurrentexecution] public class jobwraper<tjob>: IJob where Tjob:ijob, new () { private static int syncflag = 0; Private IJob Jobinner = null; Public Jobwraper () { Jobinner = activator.createinstance<tjob> (); } public void Execute (Ijobexecutioncontext context) { if (interlocked.increment (ref syncflag)! = 1) return; Busy judging try { Jobinner.execute (context); } catch (Exception ex) { master.writemsg (context. Jobdetail.key + "Execute exception:" + ex. Message + Environment.NewLine + ex. StackTrace, True, true); } Interlocked.exchange (ref syncflag, 0); Relieve Busy }
The code is very simple, the average person can understand, I just say the focus:
1.syncFlag static field, the purpose is to mark whether busy or not busy, 1 means not busy, other representative busy, The use of Interlocked.Increment and Interlocked.exchange is atomic, ensuring that only one thread can operate at a time, similar to an exclusive lock in SQL, a bit the same as lock, but different if the entire execution is wrapped in braces with lock, then the scope of the lock Relatively broad and difficult to control, and interlocked only need to be exclusive when needed, and the exclusive time is very short, the other most of the time is normal, and more manageable, which is why I like to use his reasons.
2. Why tags are busy and not busy, because I need to ensure that each execution of the business logic can be completed, and do not appear to be completed, the next execution point is again, resulting in multiple or even repeated execution.
2. Why package, because I do not want to each job class inside write try catch exception capture and busy and not busy judgment, so ordinary class only focus on business processing can. As for the wrapped class does not have to Ijob interface, you can customize all kinds of interfaces, but must have no parameter constructor, otherwise you cannot create an instance of the package class.
Through the above explanation, we should all understand, the following is my to facilitate integration management job, encapsulated a JobManager task management class, the complete code is as follows: (the code is relatively simple, no longer explained)
public class JobManager {private IScheduler scheduler = NULL; private int schedulerstate = 0; Public dictionary<string, jobwithtrigger> jobtriggers {get; Private set; } private IScheduler Getascheduler () {var stdschedulerfactory = new Stdschedulerfactory (); Scheduler = Stdschedulerfactory.getscheduler (); return scheduler; } public JobManager () {scheduler = Getascheduler (); Jobtriggers = new dictionary<string, jobwithtrigger> (); } Public Jobwithtrigger createjobwithtrigger<tjob> (string cronexpr, idictionary<string, object> jobData = null) where Tjob:ijob {var jobType = typeof (Tjob); string jobtypename = Jobtype.name; if (jobtype.isgenerictype) {jobtypename = jobtype.getgenericarguments () [0]. Name; } IJOBDEtail job = null; if (Jobdata = = null) job = Jobbuilder.create<tjob> (). Withidentity (Jobtypename). Build (); else job = jobbuilder.create<tjob> (). Withidentity (Jobtypename). Usingjobdata (New Jobdatamap (Jobdata)). Build (); Itrigger trigger = Triggerbuilder.create (). Withidentity (jobtypename + "-trigger"). Forjob (Job). Startnow (). Withcronschedule (cronexpr). Build (); var JT = new Jobwithtrigger (job, trigger); Jobtriggers[jt. Key] = JT; return JT; } public void Schedulejobs (params jobwithtrigger[] jts) {if (scheduler. IsShutDown) {scheduler = Getascheduler (); } foreach (Var jt in JTS) {scheduler. Schedulejob (JT. Jobdetail, JT. Trigger); }} public void Schedulejobs (params string[] jtkeys) {var jts = jobtriggers.where (t = Jtkeys.contains (T.key)). Select (t = = t.value). ToArray (); Schedulejobs (JTS); } public void Unschedulejobs (params triggerkey[] triggerkeys) {Scheduler. Unschedulejobs (Triggerkeys.tolist ()); } public void Unschedulejobs (params string[] jtkeys) {var triggerkeyobjs = jobtriggers.where (t = > Jtkeys.contains (T.key)). Select (t = = T.value.trigger.key). ToArray (); Unschedulejobs (TRIGGERKEYOBJS); } public int state {get {return schedulerstate; 0: Not started, 1: Start, 2: Pause, 3: Restore,-1: Stop}} [MethodImpl (methodimploptions.synchronized)] public void Start () {if (schedulerstate > 0) return; Scheduler. Start (); Schedulerstate = 1; Master.writemsg ("The Autotimingexecsystem program is started, all tasks start as scheduled. ", false, true); } [MethodImpl (methodimploptions.synchronized)] public void Stop () { if (schedulerstate <= 0) return; Scheduler. Clear (); Scheduler. Shutdown (); Schedulerstate =-1; Master.writemsg ("The Autotimingexecsystem program is stopped, all tasks stop executing.") ", false, true); } [MethodImpl (methodimploptions.synchronized)] public void Pause () {if (schedulerstate! = 1) return; Scheduler. Pauseall (); Schedulerstate = 2; Master.writemsg ("All tasks are canceled or paused. ", false, true); } [MethodImpl (methodimploptions.synchronized)] public void Resume () {if (schedulerstate!) = 2) return; Scheduler. Resumeall (); Schedulerstate = 1; Master.writemsg ("All tasks are resumed for execution. ", false, true); } }
Jobwithtrigger: Task and Trigger association classes
[Serializable] public class Jobwithtrigger {public Jobwithtrigger () } {this . Key = Guid.NewGuid (). ToString ("N"); } Public Jobwithtrigger (Ijobdetail job, Itrigger Trigger) : this () {this . Jobdetail = job; This. Trigger = Trigger; } Public Ijobdetail jobdetail {get; set;} Public Itrigger Trigger {get; set;} public string JobName { get { return this. JobDetail.Key.Name; } } public string triggername { get { return this. Trigger.Key.Name; } } public string Key { get; private set; } }
The usage is simple and the sample code is as follows:
var jobmanager = new JobManager () var jt=jobmanager.createjobwithtrigger<jobwraper<testjob>> ("0/5 * * * *?") ;//This can save or display JT to the task interface ... jobmanager.schedulejobs (Jobwithtrigger's key array or Jobwithtrigger object) Jobmanager.start (); Jobmanager.stop ();
The JobManager supports repeatedly turning on and off.
About the Quartz.net job scheduling framework of a small package, implementation of pseudo-AOP write log function