rih
rih

Reputation: 25

Multithreading in WCF with accurate timer requirement

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?

Upvotes: 0

Views: 2639

Answers (4)

Chansik Im
Chansik Im

Reputation: 1493

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?

Upvotes: 0

Justin
Justin

Reputation: 86729

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.

Upvotes: 1

Darin Dimitrov
Darin Dimitrov

Reputation: 1038780

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);

Upvotes: 1

Vitaliy Liptchinsky
Vitaliy Liptchinsky

Reputation: 5299

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

Upvotes: 0

Related Questions