Reputation: 16567
I'm writing a windows service that once started will run every X hours. The process it completes is fairly intensive, so I want to use a background worker. I'm using a Settings file to store both the hours between runs and the last time the service ran.
I'm not exactly sure the best way to do this - that is, I want the service to idle using as few resources as possible, and when it runs, it needs to run in the background worker, report what it did, and then go back into idle mode.
I've thought about using 2 background workers. The first worker would be a private local variable for the service that runs something like this:
while (true)
{
//create new background worker and run
Thread.Sleep(Settings.Default.SleepTimeHours * 3600000);
}
with a sub worker that is created each iteration of the loop, and destroyed when completed. To support cancellation, I think I would have to have a local instance of the second worker available in the service, but it will be null if the process currently is not running. When the secondary worker completes, it would send out my reporting, set the last run time in the settings file, and then dispose of the worker and set the reference to null.
I'm wondering if there is a better way to do this or a best practice.
Thanks
Upvotes: 24
Views: 52880
Reputation: 855
I typically use a Timer, then stop it when the process starts to run.
Here's an article that explains how to do it.
Upvotes: 34
Reputation: 11
After Checking lot of tutorial i developed windows service which run periodically every 60 mins and it will start process in background and run Test.exe.
public partial class Scheduler : ServiceBase
{
private Timer timer1 = null;
public Scheduler()
{
InitializeComponent();
}
// Get the Exe path
private string GetPath()
{
Assembly assembly = Assembly.GetExecutingAssembly();
return Path.Combine(Path.GetDirectoryName(assembly.Location), "Test.exe");
}
// you can right your own custom code
// Above vista version the process works as Administrator- "runas" and in old OS it will Start process as per Administartor rights
public void startprocess()
{
// System.Diagnostics.Process.Start(GetPath());
System.Diagnostics.Process process = null;
System.Diagnostics.ProcessStartInfo processStartInfo;
processStartInfo = new System.Diagnostics.ProcessStartInfo();
processStartInfo.FileName = GetPath();
if (System.Environment.OSVersion.Version.Major >= 6) // Windows Vista or higher
{
processStartInfo.Verb = "runas";
}
else
{
// No need to prompt to run as admin
}
processStartInfo.Arguments = "";
process = System.Diagnostics.Process.Start(processStartInfo);
}
// On start method will load when system Start
// timer1_tick Event will call every hour after once start the service automatically
protected override void OnStart(string[] args)
{
//System.Diagnostics.Debugger.Launch();
timer1 = new Timer();
this.timer1.Interval = 3600000; //every 60 min
this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Tick);
timer1.Enabled = true;
}
// Timer1_tick will Start process every hour
private void timer1_Tick(object sender, ElapsedEventArgs e)
{
//Write code here to do some job depends on your requirement
startprocess();
}
// Method will stop the service
// we have set the method stop service as false. so service will not stop and run every hour
protected override void OnStop()
{
timer1.Enabled = false;
}
}
Upvotes: 0
Reputation: 19
I applied a combination of the Timer and worker thread, and so far it works ok.
I set my timer to tick every minute, and, if the time matches the schedule time, I create my worker thread.
I used this technique in doing some housekeeping:
if (nowTimeVar.ToShortTimeString().CompareTo(houseKeep.ToShortTimeString()) == 0)
{
myHouseKeeper = new clsHouseKeep(archiveFolder, lastday, myCounter, myLogger);
}
Upvotes: -1
Reputation: 34539
How about something more like this:
public class LongRunningService : ServiceBase
{
System.Threading.Thread processThread;
System.Timers.Timer timer;
private Boolean Cancel;
protected override void OnStart(string[] args)
{
timer = new Timer(Settings.Default.SleepTimeHours * 3600000);
timer.Elapsed += new ElapsedEventHandler(timer_Tick);
timer.Start();
Cancel = false;
}
protected override void OnContinue()
{
timer.Start();
}
protected override void OnPause()
{
timer.Stop();
}
protected override void OnStop()
{
if (processThread.ThreadState == System.Threading.ThreadState.Running)
{
Cancel = true;
// Give thread a chance to stop
processThread.Join(500);
processThread.Abort();
}
}
void timer_Tick(object sender, EventArgs e)
{
processThread = new System.Threading.Thread(new ThreadStart(DoWork));
processThread.Start();
}
private void DoWork()
{
try
{
while (!Cancel)
{
if (Cancel) { return; }
// Do General Work
System.Threading.Thread.BeginCriticalRegion();
{
// Do work that should not be aborted in here.
}
System.Threading.Thread.EndCriticalRegion();
}
}
catch (System.Threading.ThreadAbortException tae)
{
// Clean up correctly to leave program in stable state.
}
}
}
Upvotes: 7
Reputation:
Someone had a similar question over on Super User. You could install a tool that monitors windows services. Something like Service Hawk would help you keep the services started, or allow you to schedule automatic restarts (possibly during the night) to keep the service running smoothly.
Upvotes: 2
Reputation: 46052
Use an approach based on the System.Threading.WaitHandle approach.
using System.Threading;
private Thread _thread;
private ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
private ManualResetEvent _scheduleEvent = new ManualResetEvent(false);
private System.Timers.Timer _scheduleTimer = new System.Timers.Timer();
protected override void OnStart(string[] args)
{
// Configure the timer.
_scheduleTimer.AutoReset = false;
_scheduleTimer.Interval = 120000; // 2 minutes in milliseconds
_scheduleTimer.Elapsed += delegate { _scheduleEvent.Set(); }
// Create the thread using anonymous method.
_thread = new Thread( delegate() {
// Create the WaitHandle array.
WaitHandler[] handles = new WaitHandle[] {
_shutdownEvent,
_scheduleEvent
};
// Start the timer.
_scheduleTimer.Start();
// Wait for one of the events to occur.
while (!_shutdownEvent.WaitOne(0)) {
switch (WaitHandle.WaitAny(handles)) {
case 0: // Shutdown Event
break;
case 1: // Schedule Event
_scheduleTimer.Stop();
_scheduleEvent.Reset();
ThreadPool.QueueUserWorkItem(PerformScheduledWork, null);
break;
default:
_shutdownEvent.Set(); // should never occur
}
}
} );
_thread.IsBackground = true;
_thread.Start();
}
protected override void OnStop()
{
// Signal the thread to shutdown.
_shutdownEvent.Set();
// Give the thread 3 seconds to terminate.
if (!_thread.Join(3000)) {
_thread.Abort(); // not perferred, but the service is closing anyway
}
}
private void PerformScheduledWork(object state)
{
// Perform your work here, but be mindful of the _shutdownEvent in case
// the service is shutting down.
//
// Reschedule the work to be performed.
_scheduleTimer.Start();
}
Upvotes: 5
Reputation: 1210
I had the same discussion with colleagues around 1hour ago! I went with the while(_isPolling) option, because i needed the work to happen syncronously. I didnt want the same work being picked up by other thread (the Timer approach), and implementing extra locking for that seemed like a waste.
Upvotes: 1
Reputation: 26436
Blog.StackOverflow.com has an interesting article on using cache expiration to handle periodic tasks:
https://blog.stackoverflow.com/2008/07/easy-background-tasks-in-aspnet/
Upvotes: 2
Reputation: 18933
You don't really want to use background worker. Background workers are used when you have something going one at the foreground (such as UI) and you also want something else to be done on the background. In your case there's no foreground processing, all you have is a single job that you need to run every once in a while.
Also, don't bother with the sleeping business etc. Instead, use a scheduler framework, such as Quartz.NET to wake it up every once in a while. This way when your service starts it will initialize the scheduler and do absolutely nothing until scheduler wakes up. When scheduler wakes up it will call a method of an object that you tell it to call when you initialize it.
This way the service will barely consume any CPU when idle and work full steam when needed.
Upvotes: 2
Reputation: 50235
Consider a job scheduler like Quartz.Net.
http://quartznet.sourceforge.net/
Upvotes: 10
Reputation: 755287
This is not a very good idea, since you lock your thread for the full period of "SleepTimeHours" and you won't be able to even stop the service in the meantime.
You could either make this service so that it would sleep for e.g. 5 seconds and then check whether it's time to get back to work, and if not, sleep another 5 seconds (that would give you the necessary responsiveness, if you need to stop the service).
OR: you might be better off just writing a console app that can be scheduled using the Windows "scheduled tasks" feature in order to be run every x hours. That way, you won't be blocking or using any system resource if your app isn't doing anything......
Marc
Upvotes: 15
Reputation: 3234
In my company, we rely on windows task scheduler to initiate the service, and then have the service shut itself down. This insures our service is always run at the right time and the scheduling system will allow reporting of success / failure etc.
Upvotes: 1