1

As part of Windows Service I created, I am using Timers to trigger functions every few minutes. However I want to rather trigger these functions at specific intervals inside an hour.

User Configs will determine the intervals.

Example

  • Every 10th minute call SendData() eg (2:10, 2:20, 2:30, 2:40 etc)
  • Every 55th minute call LoadData() eg (2:55, 3:55, 4:55, 6:55 etc)

So based on the above, when the Service starts up at 2:03 it needs to esnure that the first SendData will happen at 2:10 and the first LoadData will happen at 2:55.

Also while for example SendData is running, the timer will be paused to ensure that SendData is completed before the interval. Meaning that if SendData starts at 2:10 and takes 15 minutes, the next trigger will be 2:30.

LordRofticus
  • 181
  • 1
  • 2
  • 13
  • 1
    Not quite sure I am understanding your problem but: Why not have your timer trigger a function every minute that checks the minute part of the time and calls the appropriate function if required? – theduck Jun 24 '19 at 17:33
  • Meaning setting the interval of the timer to be every minute and when the timer elapse to check: If Now().Minute Mod 10 = 0 Then Call SendDate? – LordRofticus Jun 24 '19 at 17:49
  • 1
    Yes. You might also need some flag to check whether the function is still running (depending on whether you are calling the functions asynchronously) if you only ever want a function to run on its own. – theduck Jun 24 '19 at 17:51
  • Makes sense. I can while the function is running pause the Timer. Also using MOD means that I can have any kind of interval I want. – LordRofticus Jun 24 '19 at 17:53

2 Answers2

0

Here's an example w/ WinForms that you can apply to your service. We calculate the "target date" and then check the current time against it every second (or less frequently if you desire; it'll still work).

The code below is setup for a 10 minute interval, but is flexible for any duration:

Public Class Form1

    Private targetDt As DateTime

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        targetDt = GetNextTargetTime(10)
        Timer1.Interval = 1000
        Timer1.Start()
    End Sub

    Private Function GetNextTargetTime(ByVal Minutes As Integer) As DateTime
        Dim dt As DateTime = DateTime.Today.AddHours(DateTime.Now.Hour).AddMinutes(DateTime.Now.Minute)
        While (dt < DateTime.Now OrElse (dt.Minute Mod Minutes <> 0))
            dt = dt.AddMinutes(1)
        End While
        Return dt
    End Function

    Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
        If (DateTime.Now > targetDt) Then
            Timer1.Enabled = False

            ' ... do something in here ...

            targetDt = GetNextTargetTime(10)
            Timer1.Enabled = True
        End If
    End Sub

End Class
Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
  • This is what I was looking for but i think @theduck might have simpler solution. – LordRofticus Jun 24 '19 at 17:55
  • This approach will also ensure that the same trigger doesn't occur more than once per minute if the "work" is completed very quickly. It does this by starting with the seconds at zero, then increases the time until the computed target time is both past the current time and has a minute component that satisfies the MOD check for the desired interval. – Idle_Mind Jun 24 '19 at 18:01
  • Another advantage to this approach is you can store multiple target times in a list and check if any of them are past the current time. – Idle_Mind Jun 24 '19 at 18:10
  • 1
    @Idle_Mind A System.Windows.Forms.Timer may not be a very good timer to use in a Windows Service: [Best Timer for using in a Windows service](https://stackoverflow.com/a/246719/1115360). – Andrew Morton Jun 24 '19 at 18:56
  • @AndrewMorton, agreed. The approach is the same, however. I prefaced my post with this is a **WINFORMS** example. =) – Idle_Mind Jun 24 '19 at 18:58
0

Simplest solution thanks @TheDuck

In the Start of the Windows Service, set interval to minutes, and start Timer

intSendDataInterval = 10
tmrTest.Interval = 60000
tmrTest.Start()

In the elapse event (which then happens every minute) I do a mod with the current minute and the predetermined interval. If true, I stop the timer, call the function, the start the timer again.

Private Sub tmrTest_Elapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles tmrTest.Elapsed

        If Now().Minute Mod intSendDataInterval = 0 Then

            tmrTest.Stop()
            SendData()
            tmrTest.Start()            

        End If

End Sub
LordRofticus
  • 181
  • 1
  • 2
  • 13
  • Using the approach you've posted, it is actually possible to MISS a trigger if the tick would normally occur very close to the end of the desired minute, but then the system gets busy and doesn't process it until after the minute has passed. In that case, it wouldn't (possibly) fire again until the next interval hits. The approach I posted guarantees the trigger will not be missed. – Idle_Mind Jun 24 '19 at 18:12