views:

569

answers:

4

I have a WCF app that accepts requests to start a job. Each job needs to do something after exactly X minutes (e.g. 5 mins.), there can also be a job request at any time and simultaneously.

This is what I have in mind,

// WCF class  
public class RequestManager  
{
   // WCF method
   public void StartNewJob()
   {
      // start a new thread with timer for each job?
   }
}

public class Job
{
   public Job()
   {
      // do some initializations

      // do something after x mins

      // sleep or timer?
   }

   private void DoSomething()
   {
      // do some follow-ups
   }
}

With my approach, I'm afraid that there will be too many threads that's doing nothing for X mins. Per-second accuracy would be a requirement as well (say it starts a job at 0:05:01, the follow up should be at 0:10:01).

What would be the best way to approach this?

A: 

Sounds like you need the serives of the Timer class:

// WCF class  
public class RequestManager  
{
   // WCF method
   public void StartNewJob()
   {
      Job myJob = new Job();
      // Initialise myJob...
      myJob.Start();
   }
}

public class Job
{
    private Timer myTimer = new Timer();

    public Job()
    {
        myTimer.Elapsed += new ElapsedEventHandler(this.OnTimedEvent);
    }

    public void Start(int Miniutes)
    {
        myTimer.Interval = 60000 * Miniutes;
        myTimer.Enabled = true;
    }

    private static void OnTimedEvent(object source, ElapsedEventArgs e)
    {
     // So something
    }
}

The above code assumes that:

  1. You dont do anything silly like attempt to call Start() twice on the same instance of timer.
  2. There is some other non-background thread active elsewhere in the application preventing the application from closing.

Its not a full example, but hopefully it should give you the idea - the Timer class will deal with keeping time without needing a thread active for each job.

Kragen
so there is no need to start a new thread everytime?
rih
StartNewJob will be called from a web app that doesn't need to wait for the job to end. My implementation was very similar except that the myJob is started by a new thread.
rih
A: 

You need to use some timing/scheduling framework like Quartz.NET or create your own one (lightweight).

Vitaliy Liptchinsky
I'll look into Quartz.NET again. Thanks!
rih
Tried using Quartz.NET with the WCF having a static scheduler. So far, so good. What would be the advantage of using this than a new Thread with a timer?
rih
The advantage here is scalability. With Quartz.NET you'll have an ability to run several instances of your WCF service - if one instance fails, one of the others will be able to finish the work in expected time interval.
Vitaliy Liptchinsky
In a way, yes, one job fails, the others continue. I'm pretty happy with the solution right now, if there's a way to initialize the static variable from within the service, that would make it better. Right now, if I instantiate it inside the constructor (within a lock statement), I run into timeouts on the client side.
rih
A: 

I would suggest you looking at the RegisterWaitForSingleObject function:

var waitObject = new AutoResetEvent(false);

// Execute the callback on a new thread 10 seconds after this call 
// and execute it only once
ThreadPool.RegisterWaitForSingleObject(
    waitObject, 
    (state, timeout) => { Console.WriteLine("ok");  }, 
    null, 
    TimeSpan.FromSeconds(10), 
    true);

// Execute the callback on a new thread 10 seconds after this call 
// and continue executing it at 10 seconds intervals until the 
// waitHandle is signaled.
ThreadPool.RegisterWaitForSingleObject(
    waitObject, 
    (state, timeout) => { Console.WriteLine("ok");  }, 
    null, 
    TimeSpan.FromSeconds(10), 
    false);
Darin Dimitrov
A: 

Using timer seems to be good (and easier to implement) for me.

There are several timer classes you can use in .NET. Please see the following document (even though it's bit aged, but it seems to be a good start): Comparing the Timer Classes in the .NET Framework Class Library

However, you can still achieve this behavior with Thread.Sleep() as well by calculating the offset while taking timestamps on a thread wake-up and on a completion of Job.DoSomethig().

You may want to consider the followings carefully:

  1. Any contentions between threads executing Job.DoSomething()?

  2. You should be very careful in the following scenario: what if Job.DoSomething() sometimes takes more than the period (i.e. it starts at 0:05 and completes 0:13 in the example above). What does this mean to your application and how will it be handled?

    a. Total failure - abort the current(0:05) execution at 0:10 and launch 0:10 execution.

    b. Not a big deal (skip 0:10 one and run Job.DoSomething() at 0:15).

    c. Not a big deal, but need to launch 0:10 execution immediately after 0:05 task finishes (what if it keeps taking more than 5 sec??).

    d. Need to launch 0:10 execution even though 0:05 execution is currently running.

    e. anything else?

For the policy you select above, does your choice of implementation (either any of timer classes listed above or Thread.Sleep()) easy to support your policy?

Chansik Im