timing and "scheduling"

i have to write an application to do some stuff, firstly i need it to be "always on" and i need to be able to have "schedules" in the application, every 20 minutes, once or twice a day at certain time etc....

 

i guess that using a windows service and utilising the system.threading.timer class would be the best way to go about this... are there any other, maybe better, ideas about how to tackle this



Answer this question

timing and "scheduling"

  • Cesar Francisco

    At least 27 days (2^31 msec). Very little overhead.


  • ermark

    You know Windows already provides a service that does exactly this Is there any reason you can't use the windows scheduler service instead of writing your own service
  • rod_r

    I know, but I thought it might be helpful to make some suggestions about how it'll need to be overhauled.
  • SJ0775

    can that timer class handle large intervals well is there much overhead associated
  • MKan

    Hi,

    You have perfect solution what i would like to use.
    Windows service will provide you dedicated excution even when no one is logged in.
    Timer will give you interval based execution.

    Hope this help.




  • Kathy Weise

    The job scheduler API is designed to let you set schedules remotely. So that's not a reason to write your own service.

    I'd be worried about the robustness of a service that relies on in-memory state. If you ever need to restart the service for maintenance reasons, then you're hosed. Unless that is you provide a mecanism where it can get itself back into the correct state after a restart. And once you've written that mechanism do you actually have any need to leave the thing running all the time

    I'm not sure if you've decided yet on how you're actually going to communicate with your service. (E.g. .NET remoting, web services, sockets, DCOM, MSMQ, or whatever.) If I've understood, you're still trying to decide this The thing is, this may have an impact on how you structure things.

    Have you considered the following design

    The application has three parts: the database (e.g. a SQL Server Express instance); an ASP.NET web service app (or possibly WSE 3.0); a command line program run from time to time by the Windows task scheduler service.

    All the state of the application lives in the database. The list of scheduled tasks lives in the DB. Everything your app needs to remember is in there. This means that you're free to reboot, upgrade components etc with minimal disruption to service, because all the interesting state is persistent.

    The command line app connects to the DB in order to work out what tasks, if any, it should perform. So this solves your '2' requirement - that it be able to run different tasks. The command line app just looks in the DB in order to ask "What next " It can then make the decision as to what operation to perform. This means you're not relying on the scheduler to decide what to do. You're just using it as a way to run your program at the right moment, and then your program decides what operation to do.

    The web service offers whatever remote services you require. You would use this to add, modify, or remove entries. (And in fact you wouldn't be using the remote facility of the scheduler API with this particular design. The web service would edit the schedule for you.)

    This also provides you with the means to offer other operations. You can offer whatever facilities you need via the web service.

    This is essentially a stateless design. None of the bits of code remember any interesting state for longer than a single operation. All the interesting state is in the DB. (Or whatever you use to store the state. It doesn't actually have to be a DB - you could use something more lo-tech like a flat file... All that really matters is that it's persistent.) And none of your code runs for very long. Everything is of the form "What am I being asked to do What relevant information is in the DB OK, let's do it. Now update the DB if necessary. Exit."


  • Blueforce

    Ah, OK... System.Threading.Timer it is then. :)
  • Jason.J.Huang

    hey man, thanks for your useful comments....

    it’s just a POC which will need to be completely overhauled!


  • Pwint

    hey sorry i wasnt very clear about a few things...

    this is an always on application as it needs to constantly monitor input and feed output to numerous devices, so there will be an app which is always on anyway...Some of the data which needs to be checked when the schedule runs are states of these devices...

    thanks for your help by the way...


  • dcaton

    A few issues spring to mind:

    You've got a race condition here - if you happen to schedule a job that's due to run almost as soon as it's scheduled, there's a chance the timer will fire in between you constructing the ScheduleJob and attaching the event handler. Two possible solutions: 1 have a separate step to enable the job after you've attached the handler, and make it disabled when it starts, or 2 (my preferred option) require the handler to be passed in at construction time.

    You're swallowing an exception in your constructor. This is really bad. If the exception you're handling there ever actually occurs, then the code that tried to construct the object has absolutely no idea there's a problem. As far as it knows, it constructed an object, and the construction succeeded. I'd be inclined to remove that exception handling code altogether - if it fails, the code performing the construction needs to know that it failed, and the simplest way to do that is just to let the exception escape.

    You're out of step with common naming conventions. Normally an OnXxx member would be a method, and it would be associated with an Xxx event. But you've choesn to call the event OnTrigger. To be more consistent with normal .NET naming conventions, rename execute OnTrigger, rename the OnTrigger event to be Trigger, and rename the Trigger delegate to something else...speaking of which...

    Any particular reason you've introduced your own delegate type here You could be more consistent with common .NET idioms, and reduce the complexity of your code by eliminating this custom delegate type and using EventHandler instead. (Pass 'this' as the 'source' parameter and EventArgs.Empty as the second parameter.)

    I'm not sure you're gaining a whole lot from having those private tsMinute etc. fields in there. (They're also wasting space - you've made them instance fields, so every single ScheduleJob will carry around its own copy of these fields even though they never change. So at a minimum they should be static readonly, but I'd get rid of them entirely.) Why not just use code like this:

    switch (st) {
    case ScheduleType.Minute:
    tsPeriod = TimeSpan.FromMinutes(1);
    break;
    case ScheduleType.QuarterHourly:
    tsPeriod = Timespan.FromMinutes(15);
    break;
    case ScheduleType.Hourly:
    tsPeriod = Timespan.FromHours(1);
    break;
    case ScheduleType.Daily:
    tsPeriod = Timespan.FromDays(1);
    break;
    }

    I think that's at least as easy to read as what you had - using these helper functions provided by TimeSpan makes it very easy to see exactly what time spans are being used here. To my eyes, that makes it easier to see what's going on than having to work out what that tsQuarter symbols refers to.

    Why is execute public This enables anyone with a reference to a ScheduleJob object to make it look like the timer went off when in fact it hasn't. Making this public suggests to anyone using the class that it's meant to be part of the class's public API, and that they should therefore probably call it at some point. I would either make it private, or possibly make it protected virtual. (If you go with my suggestion of being more consistent with common .NET idioms, and rename this method OnTrigger, making it protected virtual would also fit in with that idiom.)

    The System.Threading.Timer class implements IDisposable. You should call Dispose on it when you're done with it. (Failure to Dispose a Timer can cause your process to grind to a halt if you use enough of them. Even if you don't hit this problem you should still Dispose the things, as otherwise you're using up a lot more resources than necessary.) Since you're using the timer in repeating mode - you've provided a period for the thing - this suggests that you need to make your ScheduleJob class implement IDisposable too. (Your ScheduleJob doesn't have a natural point where it's done with the timer. It's only done with the timer when the process is done with the ScheduleJob. So ScheduleJob should implement IDisposable.) There's no need for the full disposable pattern by the way - you absolutely don't need a finalizer here. Just implement IDisposable, and in that call Dispose on the timer.

    Your constructor doesn't notice if it's passed in an invalid value for ScheduleType. (The CLR doesn't enforce enum values to be in-range. And even if it did, you'd be advised to check for values you weren't anticipating in case someone decides to add new items to the ScheduleType and doesn't update the constructor.) So you should add a default: branch to the switch that throws an ArgumentException.

    You don't appear to have any code to manage the case where the requested start time is in the past.

    That's all I can see wrong from a brief look through the code, but once you've fixed these issues, more subtle ones may become apparent.

    Hope that's useful.


  • percent20net

    The thing is that i need my application to save state throughout its life and i need to execute methods based on the state of some of the objects. Idid not consider using the windows scheduled services for two reasons:

    1) i want the schedules to be set remotely – i.e. in a database and then have the application query the database daily and update the schedules based on the DB information.

    2) the schedules will need to perform different tasks based on other application information.

    I know that i can programmatically access the windows schedule and add and edit schedules… but is there any way to have these trigger an event within my application – I know that i could use the “System.ServiceProcess.ServiceController” to access into my service, but this seems messy to me.


  • kholling

    this is the class that will select the schedule and implement the threaded timer..

    (note: proof of concept, first draft)

    using System;
    using System.Collections.Generic;
    using System.Text;

    namespace CM12UInterface
    {
    enum ScheduleType { Minute, QuarterHourly, Hourly, Daily };

    class ScheduleJob
    {
    private TimeSpan tsMinute = new TimeSpan(0, 1, 0);
    private TimeSpan tsQuarter = new TimeSpan(0, 15, 0);
    private TimeSpan tsHour = new TimeSpan(1, 0, 0);
    private TimeSpan tsDay = new TimeSpan(1, 0, 0, 0);

    public delegate void Trigger();
    public event Trigger OnTrigger;

    private System.Threading.Timer t1;

    public ScheduleJob(DateTime startDateTime, ScheduleType st) {
    TimeSpan tsStart = startDateTime.Subtract(DateTime.Now);
    TimeSpan tsPeriod = new TimeSpan();

    switch (st) {
    case ScheduleType.Minute:
    tsPeriod = tsMinute;
    break;
    case ScheduleType.QuarterHourly:
    tsPeriod = tsQuarter;
    break;
    case ScheduleType.Hourly:
    tsPeriod = tsHour;
    break;
    case ScheduleType.Daily:
    tsPeriod = tsDay;
    break;
    }
    try
    {
    t1 = new System.Threading.Timer(new System.Threading.TimerCallback(execute), null, tsStart, tsPeriod);
    }
    catch (Exception ex) {
    EventLogEx.WriteEntry(ex.Data.ToString(), System.Diagnostics.EventLogEntryType.Error);
    }
    }

    public void execute(Object state)
    {
    OnTrigger();
    }
    }
    }

    Usage:

    //giuve you a schedule that calls “s_OnTrigger” method which starts in 5 seconds and runs once a minute

    ScheduleJob s = new ScheduleJob(DateTime.Now.AddSeconds(5), ScheduleType.Minute);

    s.OnTrigger += new ScheduleJob.Trigger(s_OnTrigger);

    //giuve you a schedule that calls “s_OnTrigger1” method which starts in 5 seconds and runs once a day (I think… obviously have not yet tested it!!!!)

    ScheduleJob s1 = new ScheduleJob(DateTime.Now.AddSeconds(5), ScheduleType.Daily);

    s1.OnTrigger += new ScheduleJob.Trigger(s_OnTrigger1);

    thanks for your input people!!


  • timing and "scheduling"