Reputation: 8652
I want to execute an .exe every morning at 4:00 AM. I did this using following code.
while (true)
{
var now = DateTime.Now;
var schedule = new DateTime(now.Year, now.Month, now.Day, 4, 00, 00);
if (schedule < now) schedule = schedule.AddDays(1);
Thread.Sleep(schedule.Subtract(now));
Process.Start("sample.exe");
}
Now I want to execute one more tasks, like deleting a particular folder and subfolders every night at 1:00 AM. So for deleting I will use code like
Directory.Delete(folder_path, recursive: true);
Should I use the same infinite loop or achieve it in some other way?
Upvotes: 1
Views: 1747
Reputation: 117009
I would use Microsoft's Reactive Framework (NuGet "System.Reactive") for this.
You can then write this handy helper method:
public IDisposable ScheduleDaily(TimeSpan when, Action what)
{
var now = DateTime.Now;
var schedule = now.Date.Add(when);
if (schedule < now)
{
schedule = schedule.AddDays(1.0);
}
return
Observable
.Timer(schedule, TimeSpan.FromDays(1.0))
.Subscribe(_ => what());
}
To call it just do this:
IDisposable runSample = ScheduleDaily(TimeSpan.FromHours(4.0),
() => Process.Start("sample.exe"));
IDisposable deleteFolders = ScheduleDaily(TimeSpan.FromHours(1.0),
() => Directory.Delete(folder_path, recursive: true));
The ScheduleDaily
returns a handy IDisposable
that you can .Dispose()
to stop the timer.
No threads or sleeping required. Very simple.
Upvotes: 2
Reputation: 12181
If all you're doing is scheduling a single .exe, you can use the Windows Task Scheduler - it's a lot easier than trying to do it yourself. If you need several tasks done at several different times, you can create several .exes. (Sorry to be brief, I'm writing this on a phone).
Upvotes: 2
Reputation: 22038
You shouldn't use Thread.Sleep for timing. Try to avoid Thread.Sleep. You can only interrupt a sleep if you abort that thread. A better way would be a ManualResetEvent.Wait()
which can be interrupted by setting it.
My advise is, use a timer:
Create a timer that is checking a list of jobs. Add the next time to execute on the job. A timer won't block the MainThread, so you can safely terminate the application. You could check the Jobs (interval) each minute or even higher.
PSEUDO!!
class MyJob
{
public DateTime ExecuteTime { get; set; }
public Action Action { get; set; }
}
List<MyJob> Jobs = new List<MyJob>();
public void TimerCallback(object sender, EventArgs e)
{
foreach (var job in Jobs)
{
if (job.ExecuteTime <= DateTime.Now)
{
try
{
job.Action();
}
catch(Exception exception)
{
// log the exception..
}
finally
{
job.ExecuteTime = job.ExecuteTime.Add(TimeSpan.FromDays(1));
}
}
}
}
// this AddJob checks if the first execute should be today or tomorrow
public void AddJob(TimeSpan executeOn, Action action)
{
var now = DateTime.Now;
var firstExec = new DateTime(now.Year, now.Month, now.Day, 4, 0, 0);
if (firstExec < now) // time for today is already passed, next day..
firstExec = firstExec.Add(TimeSpan.FromDays(1));
Jobs.Add(new MyJob { ExecuteTime = firstExec, Action = action });
}
public void Add()
{
// Add the jobs.
AddJob(new TimeSpan(4, 0, 0), RunMyJob);
AddJob(new TimeSpan(5, 0, 0), RunMyJob2);
}
public void RunMyJob()
{
// delete or move, whatever...
}
public void RunMyJob2()
{
// delete or move, whatever...
}
Upvotes: 4
Reputation: 1096
Thread.Sleep()
causes the current thread to sleep, so that thread won't be doing anything again until it's done sleeping (unless you interrupt it or something).
So, if you do it like that, you just need more threads.
var scheduleLoopAction = new Action<int, int, string>((int hour, int minute, string processName) =>
{
while (true)
{
var now = DateTime.Now;
var schedule = new DateTime(now.Year, now.Month, now.Day, hour, minute, 00);
if (schedule < now) schedule = schedule.AddDays(1);
System.Threading.Thread.Sleep(schedule.Subtract(now));
System.Diagnostics.Process.Start(processName);
}
});
((new System.Threading.Thread(() => { scheduleLoopAction(4, 0, "sample.exe"); }))).Start();
((new System.Threading.Thread(() => { scheduleLoopAction(1, 0, "sample2.exe"); }))).Start();
This probably isn't the best way accomplish the goal, but I figured that you'd wanted something like what was in the question.
Note that the thread calls don't block, so this method will finish right away (unlike the sample in the question, which'll go forever). If you want it to block, you can just have the last schedule run in the current thread, e.g.
((new System.Threading.Thread(() => { scheduleLoopAction(4, 0, "sample.exe"); }))).Start();
scheduleLoopAction(1, 0, "sample2.exe"); // Runs in current thread
Upvotes: 1