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.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
Simon
  • 3,235
  • 5
  • 36
  • 47
  • 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? – Shiv Kumar Dec 10 '10 at 14:10
  • Its windows service app. Need to every 5th minute ask some web service for data. – Simon Dec 10 '10 at 14:28

11 Answers11

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.

jason
  • 236,483
  • 35
  • 423
  • 525
  • There would need to be syncronization between the timer and the clock in order to ensure it happened at 14::05, 14:10...etc. – rcravens Dec 10 '10 at 14:16
  • @rcravens: Of course. The OP can use some simple calculations and the third parameter to establish that. – jason Dec 10 '10 at 14:20
  • There is no such thing as `System.Windows.Timer`. The timer you're showing is `System.Threading.Timer`. – Jim Mischel Dec 10 '10 at 14:34
  • @Jim Mischel: Had the correct link but the incorrect text. Thanks! – jason Dec 10 '10 at 14:36
  • @rcravens: I have added the synchronization you mentioned. – jason Dec 10 '10 at 16:47
  • 1
    @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. – Jim Mischel Dec 10 '10 at 17:16
  • And at some point you will have enough additional code to maintain that it may justify using one of the available cron job libraries. – rcravens Dec 10 '10 at 19:46
  • @rcravens 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. – Rei Miyasaka Dec 11 '10 at 00:58
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.

Community
  • 1
  • 1
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • 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. – civic.sir Jan 15 '17 at 18:41
  • @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 [mcve] that shows clearly what you've tried, and explain specifically and precisely what it is you're having trouble getting to work. – Peter Duniho Jan 15 '17 at 21:37
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.

Rei Miyasaka
  • 7,007
  • 6
  • 42
  • 69
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 :)

ykatchou
  • 3,667
  • 1
  • 22
  • 27
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
Axarydax
  • 16,353
  • 21
  • 92
  • 151
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

Machinarius
  • 3,637
  • 3
  • 30
  • 53
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);
}
Ilia
  • 31
  • 1
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
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • Is this an approach comparable to Peter Duniho's "roughly 5 minutes" solution or "exactly 5 minutes"? – MemphiZ Mar 09 '17 at 09:26
  • @MemphiZ - 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. – Enigmativity Mar 09 '17 at 10:49
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

rcravens
  • 8,320
  • 2
  • 33
  • 26
  • 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. – rcravens Dec 10 '10 at 14:18
  • Here is a google cache link to the blog post: http://webcache.googleusercontent.com/search?q=cache:AadY_OzOFRcJ:blog.bobcravens.com/2009/10/an-event-based-cron-scheduled-job-in-c/+bobcravens.com+cron+job&cd=1&hl=en&ct=clnk&gl=us – rcravens Dec 10 '10 at 14:20
  • 'cron' jobs on Windows are implemented using scheduled tasks. Either the GUI Task Scheduler tool, or the command line tools 'at' and 'schtasks'. – Jim Mischel Dec 10 '10 at 14:38
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
Qwerty01
  • 769
  • 8
  • 25
0

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

http://dotnetperls.com/timer

Few tutorials on the time in C#

jchase520
  • 77
  • 1
  • 7