14

I'm running a process in a loop which has a limit on the number of operations it does per day. When it reaches this limit I've currently got it checking the the time in a loop to see if it a new date.

Would the best option be to:

  • Keep checking the time every second for new date
  • Calculate the number of seconds until midnight and sleep that length of time
  • Something else?
sll
  • 61,540
  • 22
  • 104
  • 156
finoutlook
  • 2,523
  • 5
  • 29
  • 43
  • 1
    Use a Timer is a solution too ! – Arnaud F. Sep 16 '11 at 13:54
  • Duplicate question : http://stackoverflow.com/questions/752326/best-way-to-schedule-tasks-in-c – Olivier Sep 16 '11 at 13:57
  • I'd say its a bit different since I'm not scheduling for a particular time each day, but rather waiting for a dynamic amount of time, and their might be slightly better solutions for each – finoutlook Sep 16 '11 at 15:40

5 Answers5

17

Don't use Thread.Sleep for this type of thing. Use a Timer and calculate the duration you need to wait.

var now = DateTime.Now;
var tomorrow = now.AddDays(1);
var durationUntilMidnight = tomorrow.Date - now;

var t = new Timer(o=>{/* Do work*/}, null, TimeSpan.Zero, durationUntilMidnight);

Replace the /* Do Work */ delegate with the callback that will resume your work at the specified interval.

Edit: As mentioned in the comments, there are many things that can go wrong if you assume the "elapsed time" an application will wait for is going to match real-world time. For this reason, if timing is important to you, it is better to use smaller polling intervals to find out if the clock has reached the time you want your work to happen at.

Even better would be to use Windows Task Scheduler to run your task at the desired time. This will be much more reliable than trying to implement it yourself in code.

Dan Herbert
  • 99,428
  • 48
  • 189
  • 219
  • 2
    I'd suggest setting the timer interval to a more frequent amount than just the number of milliseconds until midnight. (What if there are time changes? etc. Might not be an issue if you go by UTC and the clock never changes). – James Johnston Sep 16 '11 at 13:56
  • Its not important for the app to be too particular about time changes, if the time changes the program will just start again at whatever the 'new' version of midnight is – finoutlook Sep 16 '11 at 15:41
7

Windows has a task scheduler that handles exactly this duty. Create the program to do that which it is supposed to do. Then set it up as a scheduled task.

P.Brian.Mackey
  • 43,228
  • 68
  • 238
  • 348
  • So you are saying do the processing for the day then end, starting up again when task scheduler says to do so? That could be a good solution although I saw the app as being something that would just run constantly (eventually a windows service) – finoutlook Sep 16 '11 at 15:43
  • @finoutlook - There is more to being a service than running for long periods of time. If it needs to be a service, then make it into one. Adding a timer won't help you convert later. – P.Brian.Mackey Sep 16 '11 at 18:05
3

Just calculate a period to wait and run an asynchronous timer in this way you can avoid extra CPU consuming whilst waiting:

var nextDateStartDateTime = DateTime.Now.AddDays(1).Subtract(DateTime.Now.TimeOfDay);
double millisecondsToWait = (nextDateStartDateTime - DateTime.Now).TotalMilliseconds;

System.Threading.Timer timer = new Timer(
    (o) => { Debug.WriteLine("New day comming on"); },
    null,
    (uint)millisecondsToWait
    0);
sll
  • 61,540
  • 22
  • 104
  • 156
  • Thanks, this is really close to the solution I'm going for (giving Dan Herbert the answer as he mentioned the timer first) – finoutlook Sep 16 '11 at 15:47
0

Considering the two options you've provided:

There are 60*60*24 = 86,400 seconds per day, so you could potentially do a lot of checking if you hit the limit early. Additionally, busy waiting is a waste of CPU cycles, and it will slow down everything else that is running.

You should calculate the number of seconds until midnight and sleep that long (although I believe the sleep paramater takes ms rather than s, so a simple conversion may be needed).

EDIT:

An additional benefit of calculating then sleeping is that if a user wants to bypass your restriction by changing the clock, they will not be able to (since the clock reading midnight won't wake the process as it would with continual checking). However, with a better understanding of how your program works internally, the user could change the clock to almost midnight every time they are about to reach the limit of operations, causing the thread to wake up in a few minutes or even a few seconds. It's a more complicated exploitation than would be doable with your first suggestion, but it can be done.

yoozer8
  • 7,361
  • 7
  • 58
  • 93
  • Thanks yeah, processing while waiting is what I wanted to avoid – finoutlook Sep 16 '11 at 13:57
  • Yes - waking up frequently is a power drain and Windows will have to keep portions of his app swapped into RAM the entire time.. Sleeping until midnight with a large initial calculation might not be accurate if the clock changes, however. Some compromise may be needed... – James Johnston Sep 16 '11 at 13:58
  • Its not too important to be accurate, and it is an internal app, so no end consumers to mess about with the times : ) – finoutlook Sep 16 '11 at 15:44
-3

This is how I make a thread sleep till tomorrow 6AM

minutesToSleep = (int)(new DateTime(DateTime.Now.AddDays(1).Year, DateTime.Now.AddDays(1).Month, DateTime.Now.AddDays(1).Day, 6, 0, 0) - DateTime.Now).TotalMinutes;
Console.WriteLine("Sleeping for {0} minutes (until tomorrow 6AM)", minutesToSleep);
Korayem
  • 12,108
  • 5
  • 69
  • 56