I have a service written in C# (.NET 1.1) and want it to perform some cleanup actions at midnight every night. I have to keep all code contained within the service, so what's the easiest way to accomplish this? Use of Thread.Sleep()
and checking for the time rolling over?

- 12,251
- 8
- 58
- 92

- 1,352
- 3
- 13
- 18
-
10You really want to make a .NET process that sits in memory, all day, and does something at midnight? Why exactly can't you, upon installing your tool, set up a system event that fires at midnight (or some other configurable time) that starts up your app, does its thing, then goes away? – Robert P Feb 02 '09 at 17:38
-
6I know I'd be a bit pissed if I found out I had a managed service consuming 20-40 megs of memory, running all the time, that does nothing except once a day. :) – Robert P Feb 02 '09 at 17:39
-
its duplicated [link](http://stackoverflow.com/questions/503564/how-might-i-schedule-a-c-sharp-windows-service-to-perform-a-task-daily) – csa Apr 25 '16 at 12:01
10 Answers
I wouldn't use Thread.Sleep(). Either use a scheduled task (as others have mentioned), or set up a timer inside your service, which fires periodically (every 10 minutes for example) and check if the date changed since the last run:
private Timer _timer;
private DateTime _lastRun = DateTime.Now.AddDays(-1);
protected override void OnStart(string[] args)
{
_timer = new Timer(10 * 60 * 1000); // every 10 minutes
_timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
_timer.Start();
//...
}
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
// ignore the time, just compare the date
if (_lastRun.Date < DateTime.Now.Date)
{
// stop the timer while we are running the cleanup task
_timer.Stop();
//
// do cleanup stuff
//
_lastRun = DateTime.Now;
_timer.Start();
}
}
-
28Wouldn't it make more sense to just set the timer to expire at midnight, instead of waking up every minute to check the time? – Kibbee Feb 02 '09 at 17:35
-
11@Kibbee: maybe if the service isn't running at midnight (for whatever reason), then you might want to execute the task as soon as the service is running again. – M4N Jun 01 '11 at 08:46
-
4Then you should add some code in the start-up of the service to figure out if you should run right away, because the service wasn't running, and it should be. You shouldn't be checking every 10 minutes continuously. Check if you should run on startup, and then if not figure out the next time to run. after that, and set a timer for however long you need to. – Kibbee Jun 01 '11 at 16:18
-
3@Kibbee: yes I agree (it's a minor detail we're discussing). But even if the timer fires every 10 seconds, it would not do any harm (i.e. it's not time-consuming). – M4N Jun 01 '11 at 19:39
-
If the timer fires every 10 seconds, even if it wakes up to do nothing, you're adding CPU load and memory pressure. This will contribute to power (battery) consumption. – Roger Lipscombe Jul 16 '12 at 11:04
-
1good solution but code above is missing _timer.start() and _lastRun should be set to yesterday like this: private DateTime _lastRun = DateTime.Now.AddDays(-1); – Zulu Z Sep 01 '13 at 14:03
-
It's not really a minor detail you're discussing. Your code is incomplete and won't ensure the task runs at midnight. _lastRun needs to be persisted beyond the life of the application instance. I've used Isolated Storage for this. I agree it should usually be a scheduled task, except in cases where it makes sense to add it into an existing service. – xr280xr Dec 13 '13 at 14:20
Check out Quartz.NET. You can use it within a Windows service. It allows you to run a job based on a configured schedule, and it even supports a simple "cron job" syntax. I've had a lot of success with it.
Here's a quick example of its usage:
// Instantiate the Quartz.NET scheduler
var schedulerFactory = new StdSchedulerFactory();
var scheduler = schedulerFactory.GetScheduler();
// Instantiate the JobDetail object passing in the type of your
// custom job class. Your class merely needs to implement a simple
// interface with a single method called "Execute".
var job = new JobDetail("job1", "group1", typeof(MyJobClass));
// Instantiate a trigger using the basic cron syntax.
// This tells it to run at 1AM every Monday - Friday.
var trigger = new CronTrigger(
"trigger1", "group1", "job1", "group1", "0 0 1 ? * MON-FRI");
// Add the job to the scheduler
scheduler.AddJob(job, true);
scheduler.ScheduleJob(trigger);

- 8,633
- 11
- 45
- 55
-
2A good suggestion - as soon as you do something even slightly more complex than the basic Timer stuff you should look at Quartz. – serg10 Feb 02 '09 at 16:59
-
2Quartz is so easy to use, I'd argue that even for a super-simple task, just go ahead and use it. It will give the ability to easily adjust the scheduling. – Andy White Feb 21 '09 at 19:29
-
Super simple is not really? Most of the newbies will catch this problem here http://stackoverflow.com/questions/24628372/quartz-job-scheduler-in-windows-service – Emil Apr 25 '16 at 13:05
-
I have created scheduler using quartz but it doesnot work when you deploy in window server... – vijay sahu May 16 '23 at 02:34
A daily task? Sounds like it should just be a scheduled task (control panel) - no need for a service here.

- 1,026,079
- 266
- 2,566
- 2,900
Does it have to be an actual service? Can you just use the built in scheduled tasks in the windows control panel.

- 5,456
- 1
- 23
- 38
-
Since the service already exists, it might be easier to add the functionality there. – M4N Feb 02 '09 at 15:41
-
1Converting a narrow purpose service such as this into a console app should be trivial. – Stephen Martin Feb 02 '09 at 15:57
-
7He already said, "I have to keep all code contained within the service". I don't feel it is necessary to question the original requirements. It depends, but sometimes there are very good reasons to use a service over a scheduled task. – jeremcc Feb 02 '09 at 16:11
-
3
-
Jerry: Perhaps, but requiring Yet Another Idle Background App on someone's computer, especially a .NET Managed app, is not cool. MAYBE if it was a C native app that, while idle, used almost no memory, but this sounds different. :) – Robert P Feb 02 '09 at 17:42
-
It depends on how many background tasks you have to manage. My current project has about 9 different jobs it perform under various schedules, and I run them all within a single windows service using Quartz.NET to do the scheduling. – jeremcc Feb 02 '09 at 20:05
-
6If I truly had only *one* task to run per day, then I guess a simple console app run with a scheduled task would be fine. My point is that it all depends on the situation, and saying "Don't use a windows service" is not a useful answer IMO. – jeremcc Feb 02 '09 at 20:06
The way I accomplish this is with a timer.
Run a server timer, have it check the Hour/Minute every 60 seconds.
If it's the right Hour/Minute, then run your process.
I actually have this abstracted out into a base class I call OnceADayRunner.
Let me clean up the code a bit and I'll post it here.
private void OnceADayRunnerTimer_Elapsed(object sender, ElapsedEventArgs e)
{
using (NDC.Push(GetType().Name))
{
try
{
log.DebugFormat("Checking if it's time to process at: {0}", e.SignalTime);
log.DebugFormat("IsTestMode: {0}", IsTestMode);
if ((e.SignalTime.Minute == MinuteToCheck && e.SignalTime.Hour == HourToCheck) || IsTestMode)
{
log.InfoFormat("Processing at: Hour = {0} - Minute = {1}", e.SignalTime.Hour, e.SignalTime.Minute);
OnceADayTimer.Enabled = false;
OnceADayMethod();
OnceADayTimer.Enabled = true;
IsTestMode = false;
}
else
{
log.DebugFormat("Not correct time at: Hour = {0} - Minute = {1}", e.SignalTime.Hour, e.SignalTime.Minute);
}
}
catch (Exception ex)
{
OnceADayTimer.Enabled = true;
log.Error(ex.ToString());
}
OnceADayTimer.Start();
}
}
The beef of the method is in the e.SignalTime.Minute/Hour check.
There are hooks in there for testing, etc. but this is what your elapsed timer could look like to make it all work.

- 5,176
- 2
- 29
- 44
-
A slight delay due to a busy server could cause it to miss the hour+minute. – Jon B Feb 02 '09 at 15:28
-
1True. When I did this, I checked : Is the time now greater than 00:00? If so, is it more than 24 hours since I last ran the job? If so, run the job, otherwise wait again. – ZombieSheep Feb 02 '09 at 15:30
As others already wrote, a timer is the best option in the scenario you described.
Depending on your exact requirements, checking the current time every minute may not be necessary. If you do not need to perform the action exactly at midnight, but just within one hour after midnight, you can go for Martin's approach of only checking if the date has changed.
If the reason you want to perform your action at midnight is that you expect a low workload on your computer, better take care: The same assumption is often made by others, and suddenly you have 100 cleanup actions kicking off between 0:00 and 0:01 a.m.
In that case you should consider starting your cleanup at a different time. I usually do those things not at clock hour, but at half hours (1.30 a.m. being my personal preference)
I would suggest that you use a timer, but set it to check every 45 seconds, not minute. Otherwise you can run into situations where with heavy load, the check for a particular minute is missed, because between the time the timer triggers and the time your code runs and checks the current time, you might have missed the target minute.

- 23,995
- 17
- 79
- 116
-
If you're not checking to ensure that it hasn't already run once, there's the potential that you could hit 00:00 twice if you're checking ever 45 seconds. – Michael Meadows Feb 02 '09 at 15:45
-
Good Point. This problem could be avoided by calling Sleep(15000) at the end of the checking procedure. (or perhaps 16000 ms, just to be on the safe side ;-) – Treb Feb 02 '09 at 16:44
-
I agree, you should check if it's already run, regardless of what timer duration you pick. – GWLlosa Feb 02 '09 at 19:38
You can also try the TaskSchedulerLibrary here http://visualstudiogallery.msdn.microsoft.com/a4a4f042-ffd3-42f2-a689-290ec13011f8
Implement the abstract class AbstractScheduledTask
and call the ScheduleUtilityFactory.AddScheduleTaskToBatch
static method
For those that found the above solutions not working, it's because you may have a this
inside your class, which implies an extension method which, as the error message says, only makes sense on a non-generic static class. Your class isn't static. This doesn't seem to be something that makes sense as an extension method, since it's acting on the instance in question, so remove the this
.

- 4,461
- 4
- 39
- 74
Try this:
public partial class Service : ServiceBase
{
private Timer timer;
public Service()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
SetTimer();
}
private void SetTimer()
{
if (timer == null)
{
timer = new Timer();
timer.AutoReset = true;
timer.Interval = 60000 * Convert.ToDouble(ConfigurationManager.AppSettings["IntervalMinutes"]);
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Start();
}
}
private void timer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
{
//Do some thing logic here
}
protected override void OnStop()
{
// disposed all service objects
}
}

- 5,278
- 43
- 65
- 115

- 19
- 3