Timer run every 5th minute
Asked Answered
V

11

21

How can I run some function every 5th minute? Example: I want run sendRequest() only at time 14:00, 14:05, 14:10 etc.

I would like to do it programmatically, in C#. The application is a Windows service.

Valerle answered 10/12, 2010 at 14:7 Comment(2)
What kind of application is it? That is, it is a GUI, Console or ASP.NET application? Have you looked at using the Windows Task Scheduler?Bumpkin
Its windows service app. Need to every 5th minute ask some web service for data.Valerle
J
15

The answer posted six years ago is useful. However, IMHO with modern C# it is now better to use the Task-based API with async and await. Also, I differ a little on the specifics of the implementation, such as how to manage the delay computation and how to round the current time to the next five minute interval.

First, let's assume the sendRequest() method returns void and has no parameters. Then, let's assume that the basic requirement is to run it roughly every five minutes (i.e. it's not that important that it run exactly on five-minute divisions of the hour). Then that can be implemented very easily, like this:

async Task RunPeriodically(Action action, TimeSpan interval, CancellationToken token)
{
    while (true)
    {
        action();
        await Task.Delay(interval, token);
    }
}

It can be called like this:

CancellationTokenSource tokenSource = new CancellationTokenSource();

Task timerTask = RunPeriodically(sendRequest, TimeSpan.FromMinutes(5), tokenSource.Token);

When tokenSource.Cancel() is called, the loop will be interrupted by the TaskCanceledException thrown at the await Task.Delay(...) statement. Otherwise, the sendRequest() method will be called every five minutes (with it being called immediately when the RunPeriodically() method is called…you can reorder the statements in the loop if you want it to wait the first time too).

That's the simplest version. If instead you do want to perform the action exactly on five minute intervals, you can do something similar, but compute the next run time and delay for an appropriate amount of time. For example:

// private field somewhere appropriate; it would probably be best to put
// this logic into a reusable class.
DateTime _nextRunTime;

async Task RunPeriodically(Action action,
    DateTime startTime, TimeSpan interval, CancellationToken token)
{
    _nextRunTime = startTime;

    while (true)
    {
        TimeSpan delay = _nextRunTime - DateTime.UtcNow;

        if (delay > TimeSpan.Zero)
        {                
            await Task.Delay(delay, token);
        }

        action();
        _nextRunTime += interval;
    }
}

Called like this:

CancellationTokenSource tokenSource = new CancellationTokenSource();
DateTime startTime = RoundCurrentToNextFiveMinutes();

Task timerTask = RunPeriodically(sendRequest,
    startTime, TimeSpan.FromMinutes(5), tokenSource.Token);

Where the helper method RoundCurrentToNextFiveMinutes() looks like this:

DateTime RoundCurrentToNextFiveMinutes()
{
    DateTime now = DateTime.UtcNow,
        result = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0);

    return result.AddMinutes(((now.Minute / 5) + 1) * 5);
}

In either example, the timerTask object can be used to monitor the state of the loop. In most cases, it's probably not needed. But if you want to be able to, e.g. await until some other part of the code cancels the loop, this object is what you'd use.

Note that the Task.Delay() method does use a timer in its implementation. The above is not suggested for the purpose of avoiding the use of a timer, but rather to show an implementation of the original goal using the modern C# async/await features. This version will mesh more cleanly with other code that is already using async/await.

Josettejosey answered 11/8, 2016 at 3:12 Comment(2)
This is very clever! However, i have one question i have which is similar to this. I need to check the DB every 5 minutes BUT it should break if either there are no updates to the DB or if a certain row message is inserted. I don't want to use SQL dependency. i really like the use of async but not sure how to go about this. Thanks.Osmious
@civic.sir: as described above, you can cancel the CancellationTokenSource for the token you pass to the method, to interrupt the timing loop. In your case, when you've determined that "there are no updates to the DB or if a certain row message is inserted", that's when you'd cancel the token to interrupt the repeating behavior. If you need more help than that, you should post a new question to the site, providing a good minimal reproducible example that shows clearly what you've tried, and explain specifically and precisely what it is you're having trouble getting to work.Josettejosey
P
21

Use System.Threading.Timer. You can specify a method to call periodically.

Example:

Timer timer = new Timer(Callback, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));

public void Callback(object state) {
    Console.WriteLine("The current time is {0}", DateTime.Now);
}

You can use the second parameter to pass state to the callback.

Note that you'll need to keep your application alive somehow (e.g., run it as a service).

As for how to make sure that it runs at hh:mm where mm % 5 == 0, you can do the following.

DateTime now = DateTime.Now;
int additionalMinutes = 5 - now.Minute % 5;
if(additionalMinutes == 0) {
    additionalMinutes = 5;
}
var nearestOnFiveMinutes = new DateTime(
    now.Year,
    now.Month,
    now.Day,
    now.Hour,
    now.Minute,
    0
).AddMinutes(additionalMinutes);
TimeSpan timeToStart = nearestOnFiveMinutes.Subtract(now);
TimeSpan tolerance = TimeSpan.FromSeconds(1);
if (timeToStart < tolerance) {
    timeToStart = TimeSpan.Zero;
}

var Timer = new Timer(callback, null, timeToStart, TimeSpan.FromMinutes(5));

Note that the tolerance is necessary in case this code is executing when now is very close to the nearest hh:mm with mm % 5 == 0. You can probably get away with a value smaller than one second but I'll leave that to you.

Proscribe answered 10/12, 2010 at 14:9 Comment(8)
There would need to be syncronization between the timer and the clock in order to ensure it happened at 14::05, 14:10...etc.Colonialism
@rcravens: Of course. The OP can use some simple calculations and the third parameter to establish that.Proscribe
There is no such thing as System.Windows.Timer. The timer you're showing is System.Threading.Timer.Homothermal
@Jim Mischel: Had the correct link but the incorrect text. Thanks!Proscribe
@rcravens: I have added the synchronization you mentioned.Proscribe
@Jason: and if you want to prevent drift (i.e. that timer is going to drift over time, and after a while it will happen at 6 minutes after), you'd make the timer a one-shot (specify Timeout.Infinite for the last parameter), re-compute the due time after each tick, and call Timer.Change with the new due time.Homothermal
And at some point you will have enough additional code to maintain that it may justify using one of the available cron job libraries.Colonialism
@Colonialism Which is why I suggested the polling method in my answer. Windows Server 2008 and Windows 7 synchronize coarse timers (>50ms), so the only processing cost in polling is a context switch and a time lookup. I mean, go ahead and recalculate the drift and instantiate a new Timer every time if you want, but clearly this problem is much simpler than that.Wellordered
J
15

The answer posted six years ago is useful. However, IMHO with modern C# it is now better to use the Task-based API with async and await. Also, I differ a little on the specifics of the implementation, such as how to manage the delay computation and how to round the current time to the next five minute interval.

First, let's assume the sendRequest() method returns void and has no parameters. Then, let's assume that the basic requirement is to run it roughly every five minutes (i.e. it's not that important that it run exactly on five-minute divisions of the hour). Then that can be implemented very easily, like this:

async Task RunPeriodically(Action action, TimeSpan interval, CancellationToken token)
{
    while (true)
    {
        action();
        await Task.Delay(interval, token);
    }
}

It can be called like this:

CancellationTokenSource tokenSource = new CancellationTokenSource();

Task timerTask = RunPeriodically(sendRequest, TimeSpan.FromMinutes(5), tokenSource.Token);

When tokenSource.Cancel() is called, the loop will be interrupted by the TaskCanceledException thrown at the await Task.Delay(...) statement. Otherwise, the sendRequest() method will be called every five minutes (with it being called immediately when the RunPeriodically() method is called…you can reorder the statements in the loop if you want it to wait the first time too).

That's the simplest version. If instead you do want to perform the action exactly on five minute intervals, you can do something similar, but compute the next run time and delay for an appropriate amount of time. For example:

// private field somewhere appropriate; it would probably be best to put
// this logic into a reusable class.
DateTime _nextRunTime;

async Task RunPeriodically(Action action,
    DateTime startTime, TimeSpan interval, CancellationToken token)
{
    _nextRunTime = startTime;

    while (true)
    {
        TimeSpan delay = _nextRunTime - DateTime.UtcNow;

        if (delay > TimeSpan.Zero)
        {                
            await Task.Delay(delay, token);
        }

        action();
        _nextRunTime += interval;
    }
}

Called like this:

CancellationTokenSource tokenSource = new CancellationTokenSource();
DateTime startTime = RoundCurrentToNextFiveMinutes();

Task timerTask = RunPeriodically(sendRequest,
    startTime, TimeSpan.FromMinutes(5), tokenSource.Token);

Where the helper method RoundCurrentToNextFiveMinutes() looks like this:

DateTime RoundCurrentToNextFiveMinutes()
{
    DateTime now = DateTime.UtcNow,
        result = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0);

    return result.AddMinutes(((now.Minute / 5) + 1) * 5);
}

In either example, the timerTask object can be used to monitor the state of the loop. In most cases, it's probably not needed. But if you want to be able to, e.g. await until some other part of the code cancels the loop, this object is what you'd use.

Note that the Task.Delay() method does use a timer in its implementation. The above is not suggested for the purpose of avoiding the use of a timer, but rather to show an implementation of the original goal using the modern C# async/await features. This version will mesh more cleanly with other code that is already using async/await.

Josettejosey answered 11/8, 2016 at 3:12 Comment(2)
This is very clever! However, i have one question i have which is similar to this. I need to check the DB every 5 minutes BUT it should break if either there are no updates to the DB or if a certain row message is inserted. I don't want to use SQL dependency. i really like the use of async but not sure how to go about this. Thanks.Osmious
@civic.sir: as described above, you can cancel the CancellationTokenSource for the token you pass to the method, to interrupt the timing loop. In your case, when you've determined that "there are no updates to the DB or if a certain row message is inserted", that's when you'd cancel the token to interrupt the repeating behavior. If you need more help than that, you should post a new question to the site, providing a good minimal reproducible example that shows clearly what you've tried, and explain specifically and precisely what it is you're having trouble getting to work.Josettejosey
W
10

Use System.Threading.Timer:

var timer = new Timer(TimerTick, null, TimeSpan.Zero, new TimeSpan(0, 0, 0, 1));

int lastMinute = 1;

void TimerTick(object state)
{
    var minute = DateTime.Now.Minutes;
    if (minute != lastMinute && minute % 5 == 0)
    {
        lastMinute = minute;
        //do stuff
    }
}

This might look somewhat clumsy and inefficient since it has to call every second, but the actual number of CPU cycles that get used to perform the check once a second is totally negligible on virtually any hardware.

(Sorry if the code isn't 100% correct; I don't have an IDE on me right now.

Wellordered answered 10/12, 2010 at 14:39 Comment(4)
Why not TimeSpan.FromSeconds(1)? It's much easier to understand.Homothermal
This is unnecessary. With some logic you can calculate the span until start and wake up every five minutes instead of polling every second.Proscribe
Jim: Oh yeah, forgot about that. Again, I don't have an IDE.Wellordered
Jason: That'll eventually drift.Wellordered
N
5

You can make a thread which include a loop like this

void Runner(){
   while(1){
         Thread t = new thread( target_method );
         t.start();
         sleep(5 * 60 * 1000);
   }
}

It's a bit quick and dirty but gonna do the job :)

Nutmeg answered 10/12, 2010 at 14:10 Comment(1)
Thread.Sleep takes its argument in milliseconds, so if you do this your computer is going to be sad...Reface
C
3

you could have a thread running that first

  1. checks how long to sleep until next 5th minute (so if it's 13:59:51, it sleeps for 9 seconds)
  2. does the action;
  3. then sleeps for 5 minutes
  4. goes to step 2
Changeup answered 10/12, 2010 at 14:9 Comment(0)
P
3

This class is everythign you need, you just setup the amount of time between delegate callings and your delegate and thats it :)

http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx

Plaice answered 10/12, 2010 at 14:9 Comment(0)
S
3

You better give you own Thread that executes the method a condition

While(programStatus == ProgramStatus.Working)
{
   While (true)
   {
      // Do your stuff
   }
   Thread.Sleep(TheTimeYouWantTheThreadToWaitTillTheNextOperation);
}
Slouch answered 18/8, 2011 at 5:4 Comment(0)
C
2

What you are looking for is code that implements 'cron' jobs. There are a few libraries out there that do this (quartz.net is one of them). I found that many of them are bulky and have many features. Here is a leaner implementation that I have used in some projects:

http://blog.bobcravens.com/2009/10/an-event-based-cron-scheduled-job-in-c/

P.S. My server appears to be down right now. I am working on that.

Hope that helps.

Bob

Colonialism answered 10/12, 2010 at 14:11 Comment(3)
The timer based solutions will generate events every 5 minutes. However, they may not be syncronized with the clock and some additional logic would be needed.Colonialism
Here is a google cache link to the blog post: webcache.googleusercontent.com/…Colonialism
'cron' jobs on Windows are implemented using scheduled tasks. Either the GUI Task Scheduler tool, or the command line tools 'at' and 'schtasks'.Homothermal
P
2

There are a few ways to do this, you can create a timer that runs every 5 minutes and start it when the time reaches one of the 5 minute intervals, or have it run every few seconds and check if the time is divisible by 5

System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); // create a new timer
timer.interval = 300000; //300000 = 5 minutes

then create a tick function and add an event handler

timer.Tick += new EventHandler(TimerTickHandler); //add the event handler
timer.Start(); //start the timer
Paymar answered 10/12, 2010 at 14:15 Comment(0)
P
2

I agree with Peter Duniho - modern C# is far better at this stuff.

I would suggest using Microsoft's Reactive Framework (NuGet "System.Reactive") and then you can do this:

IObservable<long> timer =
    Observable
        .Defer(() =>
        {
            var now = DateTimeOffset.Now;
            var result = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, 0, 0, now.Offset);
            result = result.AddMinutes(((now.Minute / 5) + 1) * 5);
            return Observable.Timer(result, TimeSpan.FromMinutes(5.0));
        });

    timer.Subscribe(x => 
    {
        /* Your code here that run on the 5 minute every 5 minutes. */
    });

I get this kind of result:

2016/08/11 14:40:00 +09:30
2016/08/11 14:45:00 +09:30
2016/08/11 14:50:00 +09:30
Pentimento answered 11/8, 2016 at 5:13 Comment(2)
Is this an approach comparable to Peter Duniho's "roughly 5 minutes" solution or "exactly 5 minutes"?Futurity
@Futurity - Windows isn't a real-time operating system. You always have drift. The best you can do is continually reschedule. The operator I've used should stay fairly close, but there is no power in the universe that will have an "exactly 5 minutes" solution. Rx does offer methods to re-calibrate the timer each time it fires. That would be the best you can do.Pentimento
W
0

http://www.csharphelp.com/2006/02/c-timer-demo/

http://dotnetperls.com/timer

Few tutorials on the time in C#

Wastepaper answered 10/12, 2010 at 14:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.