42

How can I start a background thread at a specific time of day, say 16:00?

So when the apps starts up the thread will wait until that time. But if the app starts up after that time then the thread will run straight away

ThreadPool.QueueUserWorkItem(MethodtoRunAt1600);

leppie
  • 115,091
  • 17
  • 196
  • 297
Bob
  • 4,236
  • 12
  • 45
  • 65
  • What would that thread do? If you need to run certain jobs at specific times you should check out http://www.quartz-scheduler.org/ – treze Sep 04 '13 at 10:25
  • 1
    and the .NET version is found here: http://quartznet.sourceforge.net/ – Fredrik Sep 04 '13 at 10:26
  • Look over here, there are serveral usefull answers: http://stackoverflow.com/questions/3243348/how-to-call-a-method-daily-at-specific-time-in-c – Jeroen van Langen Sep 04 '13 at 10:28
  • @FredrikRedin Thanks for the correction. Of course I meant to post a link to http://quartznet.sourceforge.net/ – treze Sep 04 '13 at 10:32
  • Is this scheduled task going to need to be cancellable? – Gusdor Sep 04 '13 at 10:37

10 Answers10

78

You can set up a timer at 16:00. I've answered a similar question here. That should help you for sure.

private System.Threading.Timer timer;
private void SetUpTimer(TimeSpan alertTime)
{
     DateTime current = DateTime.Now;
     TimeSpan timeToGo = alertTime - current.TimeOfDay;
     if (timeToGo < TimeSpan.Zero)
     {
        return;//time already passed
     }
     this.timer = new System.Threading.Timer(x =>
     {
         this.SomeMethodRunsAt1600();
     }, null, timeToGo, Timeout.InfiniteTimeSpan);
}

private void SomeMethodRunsAt1600()
{
    //this runs at 16:00:00
}

Then set it up using

SetUpTimer(new TimeSpan(16, 00, 00));

Edit: Keep the reference of the Timer as it's subject to garbage collection irrespective of the Timer is active or not.

Community
  • 1
  • 1
Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • @SriramSakthivel hi, is it possible to call the method on 3 different hours? – VAAA Sep 14 '15 at 21:58
  • @VAAA Setup 3 different timers or use single timer, once it is fired, setup the second with the same timer and so on. – Sriram Sakthivel Sep 15 '15 at 06:10
  • @SriramSakthivel Can you explain this further? How to this will work. and how to use this. – Reynan Jun 08 '16 at 07:43
  • and does it reset every day? will it run on the next day? – Reynan Jun 08 '16 at 08:33
  • 1
    @Reynan No it doesn't. To set it for everyday, you need to pass `Timeout.FromDays(1)` instead of `Timeout.InfiniteTimeSpan` in the `Timer` constructor's last parameter. Also note that you may need to alter the if statement which skips firing the timer if time has already crossed. – Sriram Sakthivel Jun 08 '16 at 10:18
  • Your calculation will fail if I want to start the task at 00:00 every day –  Nov 24 '16 at 07:24
  • @SriramSakthivel That is TimeSpan.FromHours(1) not Timeout.FromDays(1) – dvjanm Jun 07 '17 at 08:29
  • @jannagy02 He wants to run every day at same time. In which case `Timeout.FromDays(1)` is what he need. – Sriram Sakthivel Jun 08 '17 at 17:44
  • @SriramSakthivel Sorry, FromHours was mistake, I only wanted to write that Timeout has no FromDays, TimeSpan has. – dvjanm Jun 08 '17 at 18:08
  • 1
    @LeonardAB I recommend to use windows Task scheduler or quartz task scheduler instead – Sriram Sakthivel May 31 '19 at 18:25
12

I would use Job Scheduling Library like Quartz or simply create console application and run it using windows task scheduler at the specific time of the day.

Why not just use System.Timers.Timer?

  • Timers have no persistence mechanism.
  • Timers have inflexible scheduling (only able to set start-time & repeat interval, nothing based on dates, time of day, etc.
  • Timers don't utilize a thread-pool (one thread per timer)
  • Timers have no real management schemes - you'd have to write your own mechanism for being able to remember, organize and retreive your tasks by name, e
Damith
  • 62,401
  • 13
  • 102
  • 153
  • 7
    Why install an entire library for something which is trivial to do with a `Timer`? – jgauffin Sep 04 '13 at 10:36
  • 1
    Why install an entire library, or create-then-cleanup a timer, for a one time "wait" mechanism that is 2 lines of code :| – Timothy Khouri Sep 04 '13 at 11:14
  • @jgauffin: Because a timer is subject to error caused by changes in the system clock. If you set a wait for 12 hours and the time changes (Daylight Savings, NTP update, user changes time), then your event won't happen at the desired time. So the thing you thought would happen at 3:00 AM happens at 4:00 AM ... or 2:00 AM. – Jim Mischel Sep 04 '13 at 13:07
  • @Damith: Timers do use the thread pool. At least System.Threading.Timer. – jgauffin Sep 04 '13 at 13:20
  • @JimMischel: Explain how. The OP want to execute at a specific time. One must assume that the system time is correct. The edge case with DST change or clock changes must be implemented with business rules that only the OP knows of (DST is only a problem if the time is set to the time when DST changes). – jgauffin Sep 04 '13 at 13:22
  • 2
    @jgauffin: Say desired time is 0400. At 1600, you set a timer for 12 hours. At 0200, the timer has 2 more hours to go. But it's "Fall Back" day and the clock is set back one hour. So now it's 0100 and the timer will fire at 0300. Same sort of thing happens on "Spring Ahead" day. The timer will fire at 0500 because the clock was moved ahead. The point is that the system time *is* correct. The mistake is assuming that 12 hours of real time is always 12 hours of clock time. – Jim Mischel Sep 04 '13 at 13:31
  • The timer is not set for 12 hours. It's set to execute at 16:00 – jgauffin Sep 04 '13 at 13:53
  • 2
    @jgauffin: You can't set a `System.Timers.Timer` or any of the other .NET timers for a specific time. You can only set them for a time period. – Jim Mischel Sep 04 '13 at 13:58
  • 2
    @Damith: Your point about timers not using the thread pool is incorrect. A timer does not occupy a thread except when its callback function is being executed, and then it executes on a pool thread. It's not like there would be 100 threads allocated if you created 100 timers. – Jim Mischel Sep 04 '13 at 14:02
  • @JimMischel: I'm not saying that you can. I'm saying that the problem is still easy to solve with a timer. – jgauffin Sep 04 '13 at 14:05
  • @Damith: Timers do use the ThreadPool : https://learn.microsoft.com/en-us/dotnet/api/system.threading.timer?view=netframework-4.8 – Elo Feb 04 '20 at 09:09
9

you can do it with a timer:

public class OncePerDayTimer : IDisposable
{
    private DateTime _lastRunDate;
    private TimeSpan _time;
    private Timer _timer;
    private Action _callback;

    public OncePerDayTimer(TimeSpan time, Action callback)
    {
        _time = time;
        _timer = new Timer(CheckTime, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
        _callback = callback;
    }

    private void CheckTime(object state)
    {
        if (_lastRunDate == DateTime.Today)
            return;

        if (DateTime.Now.TimeOfDay < _time)
            return;

        _lastRunDate = DateTime.Today;
        _callback();
    }

    public void Dispose()
    {
        if (_timer == null)
            return;

        _timer.Dispose();
        _timer = null;
    }
}
georgiosd
  • 3,038
  • 3
  • 39
  • 51
jgauffin
  • 99,844
  • 45
  • 235
  • 372
6

Any solution that depends on System.Timers.Timer, System.Threading.Timer, or any of the other timers that currently exist in the .NET Framework will fail in the face of Daylight Saving time changes. If you use any of those timers, you will have to do some polling.

Windows has a Waitable Timer that you can use, but it's not supported by any Framework class. I wrote a wrapper for it some years ago. The article I published is no longer available, but you can download the full source code from http://www.mischel.com/pubs/waitabletimer.zip

That said, if the only thing your program does is run once every day, or if the task it performs can be split off from the rest of the program, you're almost certainly better off with a scheduled task. And although I haven't ever used Quartz.NET, I have no problem recommending it based on the good reviews I've seen from people whose judgement I trust.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
2

A very simple solution:

if (DateTime.Now.Hour > 16)
{
    MethodtoRunAt1600();
}
else
{
    var next16 = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 16, 0, 0);
    var timer = new Timer(MethodtoRunAt1600, null, next16 - DateTime.Now, TimeSpan.FromHours(24));
    timer.Start()
}
Jordy Langen
  • 3,591
  • 4
  • 23
  • 32
1

If you're looking for something "quick and dirty"... then you could just start your thread in the thread pool, and "wait" for the right time.

// By the way, this code would be *INSIDE* of the background thread. That's what
// the sentence above says, but apparently we devs only read code :)
while (DateTime.Now.TimeOfDay < myDesiredStartTime)
{
    Thread.Sleep(1000);
}
Timothy Khouri
  • 31,315
  • 21
  • 88
  • 128
  • 12
    Lolling at the thought of someone calling that from the UI thread... – Martin Milan Sep 04 '13 at 10:39
  • 1
    This is the simplest answer - instead of having the main thread decide when it is time for the background thread to start, just start the background thread right away and let it decide when it is time to execute its function. – mbeckish Sep 04 '13 at 10:58
  • 1
    @MartinMilan - No, you would not call that from the UI thread. Apparently you didn't read the very short sentence above the code snippet :) – Timothy Khouri Sep 04 '13 at 11:10
  • 4
    No Timothy - I wasn't suggesting you would advocate this - just that I could imagine someone (inexperienced) trying it... Timothy - meet sense of humour... – Martin Milan Sep 04 '13 at 12:55
1

Here's a simple way to schedule a method to be called once, either immediately or at 16:00.

var runAt = DateTime.Today + TimeSpan.FromHours(16);
if(runAt < DateTime.Now)
{
    MethodtoRunAt1600();    
}
else
{
    var dueTime = runAt - DateTime.Now;
    var timer = new System.Threading.Timer(_ => MethodtoRunAt1600(), null, dueTime, TimeSpan.Zero); 
} 
Steve Ruble
  • 3,875
  • 21
  • 27
0

Can we use like this?

DispatcherTimer timer ;
// When application is started
if (DateTime.Now.Hour == 16)
{
   // Start your task
}
else
{
    timer = new DispatcherTimer();
    timer.Interval = new TimeSpan(0, 1, 0);
    timer.Tick += timer_Tick;
    timer.Start();
}

Then check for every one minute..

void timer_Tick(object sender, EventArgs e)
{
    if (DateTime.Now.Hour == 16)
    {
        // Start your task
    }
}
Tshilidzi Mudau
  • 7,373
  • 6
  • 36
  • 49
Naren
  • 2,231
  • 1
  • 25
  • 45
  • 1
    I realise this is a few years old, however, Would this not run the task 60 times (once every minute) during the hour? – DDuffy May 16 '17 at 13:16
0

1- Call your application from another process

System.Diagnostics.Process.Start("CMD MyApplication.exe", "/C TIME 16:00");

2- Use Timer and each 1 minute check the current time.

3- Handle the Windows time changed event in your appliction and each time also check the current time more info:

http://msdn.microsoft.com/en-us/library/microsoft.win32.systemevents.timechanged%28v=vs.100%29.aspx

I hope one of thoese will help you.

Bassam Alugili
  • 16,345
  • 7
  • 52
  • 70
0

This is something i used for achieving this kind of functionality. http://www.codeproject.com/Articles/12117/Simulate-a-Windows-Service-using-ASP-NET-to-run-sc

Ali Umair
  • 1,386
  • 1
  • 21
  • 42