Introduction
Every typical server or service application need scheduling of some events inside the application. These events generally are supposed to wake up at certain determined times to do self checks, check statuses of threads, or typically refresh resources once in a while, and so on. This requires shorter and long time scheduling of events inside the application. The most common approach is to use timers for different tasks and attach them to threads. A scheduler component is presented in this article for simplifying a way to create and maintain event scheduling inside an application.
Using the code
The best example to create and use the Schedule classes is in the demo application. This piece of code inside the demo creates all the types of schedule objects implemented in the library, and also provides a delegate ScheduleCallBack()
for the schedule to call back for OnTrigger
event.
Schedule s = new IntervalSchedule("Test_Interval",
DateTime.Now.AddMinutes(1), 45, TimeSpan.Zero,
new TimeSpan(TimeSpan.TicksPerDay));
s.OnTrigger += new EventScheduler.Invoke(ScheduleCallBack);
Scheduler.AddSchedule(s);
s = new OneTimeSchedule("Test_Onetime", DateTime.Now.AddMinutes(1.5));
s.OnTrigger += new EventScheduler.Invoke(ScheduleCallBack);
Scheduler.AddSchedule(s);
s = new DailySchedule("Test_daily", DateTime.Now.AddMinutes(2));
s.OnTrigger += new EventScheduler.Invoke(ScheduleCallBack);
Scheduler.AddSchedule(s);
s = new WeeklySchedule("Test_weekly", DateTime.Now.AddMinutes(2.5));
s.OnTrigger += new EventScheduler.Invoke(ScheduleCallBack);
Scheduler.AddSchedule(s);
s = new MonthlySchedule("Test_monthly", DateTime.Now.AddMinutes(3));
s.OnTrigger += new EventScheduler.Invoke(ScheduleCallBack);
Scheduler.AddSchedule(s);
As can be seen, the three main lines are to:
- create a
Schedule
instance
- subscribe to the
OnTrigger
event, and
- add the
Schedule
to the Scheduler
's list
That's it! To see this in action, run the demo application shown below and click on Test Code button. This will create an instance of each schedule and gives a feeling of what the scheduler can do.
Design and Code details
The following class diagram shows the different classes in the scheduler library and their relationship.
At the root of the library is the Schedule
class which implements IComparable
interface. The IComparable
interface provides the CompareTo(Object)
method which is used to compare two Schedule
objects for sorting the list of schedules to determine the sequence of invocation times for the timer.
The library already provides general Schedules like OneTimeSchedule
(used for raising an event only once), IntervalSchedule
(used to raise an event at regular intervals), DailySchedule
, WeeklySchedule
and MonthlySchedule
, which are self explanatory. The Schedule
base class has generic properties like Name
, Type
, NextInvokeTime
etc. used by all derived objects, and some specific properties like Interval
which is used in this case only by IntervalSchedule
.
These are the steps through which the Scheduler
goes through for scheduling an event:
- A static timer (
Scheduler.Timer
) with a call back method (DispatchEvents
) is created and initially put to sleep.
- Schedules are created either by code or by GUI. Individual conditions are checked in the
Schedule
's constructor.
- When ever a
Schedule
object is added to the Scheduler
using AddSchedule(Schedule s)
method:
- the list is sorted. The list which is an
ArrayList
in turn uses the Schedule.CompareTo(Object)
method to determine the sorting order.
- the
NextInvokeTime
of the first element in the list is used by the Timer
to decide when to wake up next.
- When the
Timer
wakes up:
- it calls
DispatchEvents
call back
- which calls
TriggerEvents()
on the first Schedule
object in the list
A Schedule
's view GUI is also provided with the library to manage the schedules through a GUI as a singleton class (ScheduleUI
) which is created by the static method ScheduleUI.ShowSchedules()
. This GUI uses the Scheduler.OnSchedulerEvent
event provided by Scheduler
to track when a Schedule
is created, deleted, or invoked.
Typically, this GUI can be used for administration of schedules and also to create or delete them easily. Once a schedule is created, it can be accessed programmatically using Scheduler
events or the name of the Schedule
itself.
For e.g., the following code inside ScheduleUI
class shows how the Scheduler
events are used to refresh the list of Schedule
s.
public void OnSchedulerEvent(SchedulerEventType type, string scheduleName)
{
switch(type)
{
case SchedulerEventType.CREATED:
ListViewItem lv = SchedulesView.Items.Add(scheduleName);
Schedule s = Scheduler.GetSchedule(scheduleName);
lv.SubItems.Add(s.Type.ToString());
lv.SubItems.Add(s.NextInvokeTime.ToString("MM/dd/yyyy hh:mm:ss tt"));
break;
case SchedulerEventType.DELETED:
for (int i=0; i<SchedulesView.Items.Count; i++)
if (SchedulesView.Items[i].Text == scheduleName)
SchedulesView.Items.RemoveAt(i);
break;
case SchedulerEventType.INVOKED:
for (int i=0; i<SchedulesView.Items.Count; i++)
if (SchedulesView.Items[i].Text == scheduleName)
{
Schedule si = Scheduler.GetSchedule(scheduleName);
SchedulesView.Items[i].SubItems[2].Text =
si.NextInvokeTime.ToString("MM/dd/yyyy hh:mm:ss tt");
}
break;
}
SchedulesView.Refresh();
}
Another critical piece of code is the bool[] m_workingWeekDays
array in the Schedule
base class, in which the active week days are stored. The bool NoFreeWeekDay()
and bool CanInvokeOnNextWeekDay()
use this array to determine if a schedule can run on a week day.
Similarly, the bool IsInvokeTimeInTimeRange()
determines if a schedule can run in a time range on any given day. Please refer to comments in the code if you are interested to get into the details.