自製簡易排程器(1)

有些時候我們在程式中會用到排程器的功能,說起來排程器其實就是在應用Timer的功能,我知道網路上其實有滿多這種現成的排程器類別可用,不過個人覺得自己動手寫來的有趣點。

        有些時候我們在程式中會用到排程器的功能,說起來排程器其實就是在應用Timer的功能,我知道網路上其實有滿多這種現成的排程器類別可用,不過個人覺得自己動手寫來的有趣點。在這個系列我會用System.Threading.Timer類別來實作,如果對SystemThread.Timer不太清楚的話,可以參考前面的三種時間人《.NET中的Timer(3)》

        第一篇先介紹邏輯上最直覺的作法,小弟是不瞭解大家的直覺是不是和我一樣,不過就我個人有點莫名其妙的邏輯,第一時間內想到的就是每隔一段時間去檢查是否已到達排程的工作該執行的時間;這個一段時間和設定的最小單位有關,如果精確度只到分鐘那就是每60秒檢查一次,不過以下的例子先以秒為最小單位來示範。

        在這個範例中有用到一個回呼的State Object,由於System.Threading.Timer是以回呼的方式來執行其程序,而且我們有需要傳一些變數之類的給它的回呼函式,所以先建立了一個簡單的列舉和自訂類別,這個CSState類別中的變數說明如下:﹝如果不是很瞭解為何要設這個類別,可以參考回呼的秘密花園《State Object》﹞。

Cycle變數 :執行週期,每小時/每日/每週/每月
iWeekDay變數 :當週期為Weekly﹝每週一次﹞時,此變數代表在星期幾執行
iDay變數:當週期為Monthly﹝每月一次﹞時,此變數代表在每月幾日執行
iHour變數 :指定執行的時﹝當週期為Hourly/每小時一次時此變數無效﹞
iMinute變數 :指定執行的分
iSecond變數 :指定執行的秒Sch1_01

Private Class CSState
      Enum Period
          Hourly = 0
          Daily = 1
          Weekly = 2
          Monthly = 3
      End Enum
      Public Cycle As Period
      Public iWeekDay As DayOfWeek
      Public iDay As Integer
      Public iHour As Integer
      Public iMinute As Integer
      Public iSecond As Integer
  End Class

         整個畫面的構造很簡單,沒有什麼特別的。

         程式的重點在於建立一個CSState與System.Threading.Timer的執行個體 ,使用Timer 的建構函式 (TimerCallback, Object, Int32, Int32),並且將CSState的執行個體﹝也就是myStateObj﹞當成是Timer建構函式中的第二個參數傳入。

[建立myStateObj並設定其變數值]
Dim myStateObj As New CSState
myStateObj.Cycle = ComboBox1.SelectedIndex
myStateObj.iWeekDay = ComboBox2.SelectedIndex
myStateObj.iDay = NUD_Date.Value
myStateObj.iHour = NUD_Hour.Value
myStateObj.iMinute = NUD_Minute.Value
myStateObj.iSecond = NUD_Second.Value
[建立myTimer委派test函式為其回呼函式,並將myStateObj傳入]
Dim myTimer As New System.Threading.Timer(AddressOf test, myStateObj, -1, 0)
[立刻呼叫myTimer的TimerCallBack並且每秒重覆執行一次呼叫]
myTimer.Change(0, 1000)

         然後就是在test函式中判斷週期參數,依不同的週期特性判斷是否已到達排程的時間,並在條件符合的狀況下執行程序:

Private Sub test(ByVal state As Object)
        Dim TimeNow As Date
        TimeNow = Now()
        Dim myStateObj As CSState
        myStateObj = CType(state, CSState)
        Select Case myStateObj.Cycle
            Case 0
                If TimeNow.Minute = myStateOBJ.iMinute AndAlso TimeNow.Second = myStateOBJ.iSecond Then
                    DisplayMsg2("End:" & Now.ToString("yyyy/MM/dd HH:mm:ss.fffffff") & "(Hourly)")
                End If
            Case 1
                If TimeNow.Hour = myStateOBJ.iHour AndAlso TimeNow.Minute = myStateOBJ.iMinute AndAlso TimeNow.Second = myStateOBJ.iSecond Then
                    DisplayMsg2("End:" & Now.ToString("yyyy/MM/dd HH:mm:ss.fffffff") & "(Daily)")
                End If
            Case 2
                If TimeNow.DayOfWeek = myStateObj.iWeekDay AndAlso TimeNow.Hour = myStateObj.iHour AndAlso TimeNow.Minute = myStateObj.iMinute AndAlso TimeNow.Second = myStateObj.iSecond Then
                    DisplayMsg2("End:" & Now.ToString("yyyy/MM/dd HH:mm:ss.fffffff") & "(Weekly)")
                End If
            Case 3
                If TimeNow.Day = myStateObj.iDay AndAlso TimeNow.Hour = myStateObj.iHour AndAlso TimeNow.Minute = myStateObj.iMinute AndAlso TimeNow.Second = myStateObj.iSecond Then
                    DisplayMsg2("End:" & Now.ToString("yyyy/MM/dd HH:mm:ss.fffffff") & "(Monthly)")
                End If
        End Select
    End Sub

    這個範例當然不是挺理想,第一個不太理想的地方是它每秒去做一次判斷,雖然感覺上這個判斷的行為不怎麼消耗資源,但總覺得太麻煩了些;第二個是沒把排程器單獨寫成一個類別,就重複使用的觀點上而言就不是挺方便的了。這個範例程式照舊是以VB.NET 2005撰寫,可以在以下連結下載:SchedulerTest0.rar