65.9K
CodeProject 正在变化。 阅读更多。
Home

用于应用程序内部事件调度的组件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (60投票s)

2004 年 10 月 1 日

3分钟阅读

viewsIcon

235093

downloadIcon

6862

本文介绍了一个用于在服务器或服务应用程序内部调度事件的设计和现成的组件。

引言

每个典型的服务器或服务应用程序都需要在应用程序内部调度一些事件。这些事件通常会在特定的预定时间被触发,以进行自我检查、检查线程状态,或者定期刷新资源等等。这需要在应用程序内部进行短期和长期的事件调度。最常见的方法是为不同的任务使用计时器并将它们附加到线程。本文介绍了一个调度器组件,用于简化在应用程序中创建和维护事件调度的方法。

使用代码

创建和使用 Schedule 类的最佳示例是演示应用程序。演示中的这段代码创建了库中实现的所有类型的调度对象,并提供了一个委托 `ScheduleCallBack()`,供调度在 `OnTrigger` 事件时回调。

// create and add different types of schedules
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);

正如所见,主要有三个步骤:

  • 创建 `Schedule` 实例
  • 订阅 `OnTrigger` 事件,以及
  • 将 `Schedule` 添加到 `Scheduler` 的列表中

就这样!要查看实际效果,请运行下面所示的演示应用程序,然后单击“**Test Code**”按钮。这将创建每个调度的实例,让您体会调度器可以做什么。

设计和代码细节

下面的类图显示了调度器库中的不同类及其关系。

库的根类是 `Schedule` 类,它实现了 `IComparable` 接口。`IComparable` 接口提供了 `CompareTo(Object)` 方法,该方法用于比较两个 `Schedule` 对象,对调度列表进行排序,以确定计时器的调用顺序。

该库已经提供了通用的调度类,例如 `OneTimeSchedule`(用于仅触发一次事件)、`IntervalSchedule`(用于定期触发事件)、`DailySchedule`、`WeeklySchedule` 和 `MonthlySchedule`,这些类名一目了然。`Schedule` 基类具有所有派生对象使用的通用属性,如 `Name`、`Type`、`NextInvokeTime` 等,以及一些特定属性,如 `Interval`,此属性仅由 `IntervalSchedule` 使用。

以下是 `Scheduler` 在调度事件时经历的步骤:

  • 创建一个静态计时器(`Scheduler.Timer`)并带有一个回调方法(`DispatchEvents`),然后将其初始设置为休眠状态。
  • 调度可以通过代码或 GUI 创建。`Schedule` 的构造函数中会检查各个条件。
  • 每当使用 `AddSchedule(Schedule s)` 方法将 `Schedule` 对象添加到 `Scheduler` 时:
    • 列表会被排序。列表是一个 `ArrayList`,它反过来使用 `Schedule.CompareTo(Object)` 方法来确定排序顺序。
    • 列表中的第一个元素的 `NextInvokeTime` 被 `Timer` 用来决定下一次唤醒的时间。
  • 当 `Timer` 唤醒时:
    • 它会调用 `DispatchEvents` 回调。
    • 该回调会调用列表中第一个 `Schedule` 对象的 `TriggerEvents()`。

库中还提供了一个 `Schedule` 的视图 GUI,通过一个单例类(`ScheduleUI`)来管理调度,该类通过静态方法 `ScheduleUI.ShowSchedules()` 创建。此 GUI 使用 `Scheduler` 提供的 `Scheduler.OnSchedulerEvent` 事件来跟踪 `Schedule` 的创建、删除或调用。

通常,此 GUI 可用于调度管理,并且可以轻松创建或删除它们。一旦创建了一个调度,就可以通过 `Scheduler` 事件或 `Schedule` 本身的名称以编程方式访问它。

例如,`ScheduleUI` 类中的以下代码显示了如何使用 `Scheduler` 事件来刷新 `Schedule` 列表。

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();
}

另一段关键代码是 `Schedule` 基类中的 `bool[] m_workingWeekDays` 数组,其中存储了活动的星期几。`bool NoFreeWeekDay()` 和 `bool CanInvokeOnNextWeekDay()` 使用此数组来确定调度是否可以在工作日运行。

同样,`bool IsInvokeTimeInTimeRange()` 确定调度是否可以在任何给定日期的时间范围内运行。如果您有兴趣了解更多细节,请参阅代码中的注释。

© . All rights reserved.