Josh
Josh

Reputation: 16567

Windows Service that runs Periodically

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

Answers (13)

CitizenBane
CitizenBane

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

mihir patel
mihir patel

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

Tom
Tom

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

Ian
Ian

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

user188162
user188162

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

Matt Davis
Matt Davis

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

David Kiff
David Kiff

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

Keltex
Keltex

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

Andriy Volkov
Andriy Volkov

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

Austin Salonen
Austin Salonen

Reputation: 50235

Consider a job scheduler like Quartz.Net.

http://quartznet.sourceforge.net/

Upvotes: 10

marc_s
marc_s

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

RichardOD
RichardOD

Reputation: 29157

Use a timer to do this.

Upvotes: 1

JustLoren
JustLoren

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

Related Questions