Multithreading: running tasks at a specific time

As we’ve seen, we can also use the thread pool for executing a (possible recurring) task at a specific time. To do that, we need to take a look at the Timer class. Initializing a timer is done through one of its constructors:

public Timer(TimerCallback callback);
public Timer(TimerCallback callback, object state, int dueTime, int period);
public Timer(
    TimerCallback callback, object state, long dueTime, long period);
public Timer(
   TimerCallback callback, object state,
   TimeSpan dueTime, TimeSpan period);
public Timer(
   TimerCallback callback,object state,uint dueTime, uint period);

As you can see, all accept a delegate of type TimerCallback delegate which will be invoked (possibly, several times) by one of thread pool’s thread:

public delegate void TimerCallback(object state);

The delegate receives the object you pass through the state parameter of the constructor (ie, when you use a constructor that uses that parameter; in the other cases, you’ll get null).

The dueTime parameter lets you specify the first time the timer will invoke the callback delegate. The int and double overloads let you specify that info in milliseconds. The other overload lets you use  TimeSpan for specifying that interval. For creating a timer that fires immediately, you should pass 0. Notice also that you can pass –1 for this parameter (that’s what the constructor that receives only the delegate parameter does). Doing it results in creating a disabled timer (which can be latter enabled through the call of the Change method).

The period parameter (which, again, can be defined in milliseconds – int or long overloads – or through a specific TimeSpan) is there for setting the interval between invocations of the specified callback. If you don’t want to have recursive fires, then you should pass the Timeout.Infinite (-1) to that parameter.

Here’s the most basic snippet of code that shows its usage:

var timer = new Timer(
                obj => Console.WriteLine(DateTime.Now),
                null, //no state
                0, //start right away,
                1000 //repeat each second
                );
Thread.Sleep(4000); //sleep main thread

The previous example instantiated a new timer which prints the current date and time. It fired right away (notice the 0 to the dueTime parameter) and repeated the callback invocation every second (until the program ended). If we wanted to be perfectionists, then we should wrap the timer in using block (or, alternatively, use a try/finally block) so that it gets disposed when we’re done with it.

It’s important to know that there are 2 overloads of the Dispose method. The simple no parameter version stops the timer from firing in the future and releases it. However, there’s still the problem of having tasks that are executing or waiting to be executed in the thread pool (queue). If you’re worried with that and would prefer them to be run before exiting, then you can use the other overload which receives a WaitHandle reference that will be signaled when all the waiting tasks have been executed. Take a look at the following code:

var evt = new ManualResetEvent(false);
var timer = new Timer(
     obj => { Thread.Sleep(1000); Console.WriteLine(DateTime.Now); },
     null, //no state
     0, //start right away,
     1000 //repeat each second
     );
timer.Dispose(evt);
evt.WaitOne();

If you run it, you’ll see that the event will get signaled when the 1st timer’s callback execution ends. Without the event, you wouldn’t see anything printed because the program would terminate immediately (remember that timers use thread pool’s thread and that these are background threads). Btw, there’s an interesting trick I’ve picked up from Joe Duffy’s book (which is *really really* recommended reading): create a dumb WaitHandle instead of using one of the existing ones that rely on kernel objects:

class DumbHandle: WaitHandle{}
var evt = new DumbHandle();
//some code as before

After instantiating a timer, you can enable or disable it through the Change method. Btw, you can also use this method to change the firing intervals at which the callback delegate will be executed. Here’s some code that disables a timer and then enables it:

var timer = new Timer(
                obj => { Console.WriteLine(DateTime.Now); },
                null, //no state
                0, //start right away,
                1000 //repeat each second
                );
timer.Change(-1, -1); //disable timer
Thread.Sleep(3000);
timer.Change(0, -1); //enable and fire right away

Before ending, there’s still time for pointing on interesting gotcha: you shouldn’t take more time in the callback delegate than the repetition interval you’ve set up. Doing that might lead to creating lots and lots of “unneeded” threads which will end up causing the degradation of your app. Just try the following code:

var worker = 0;
var io = 0;           
Console.WriteLine( "worker: {0}, io: {1}", worker, io );
var timer = new Timer(
      obj => {
                  Console.WriteLine(Thread.CurrentThread.ManagedThreadId); 
                  Thread.Sleep(30000); },
     null, //no state
     0, //start right away,
     1 //repeat each second
     );          
Thread.Sleep(40000);
ThreadPool.GetAvailableThreads(out worker, out io);
Console.WriteLine("worker: {0}, io: {1}", worker, io);

Ok, that should be enough for making a point, right? (in my machine, I’ve started with 0 worker threads and ended with more than 400 threads). btw, the same goes for locking thread pools used by  timers (ie, make sure they don’t block longer than the firing interval you’ve specified).

And that’s all for today. Keep tuned for more on multithreaded apps…

Advertisements

~ by Luis Abreu on June 1, 2009.

6 Responses to “Multithreading: running tasks at a specific time”

  1. Looking for the best in free streaming movies? Check out http://www.nowflicks.com/ for all the newest and FREE streaming videos and t.v. shows

  2. Hi everybody,

    My name is Allan, I am 41 yrs old, living in Fort Worth, TX.

    I”d love to make good close friends here.

    Thanks,
    Allan.

  3. Hi everybody,

    My name is Eva, I am 41 yrs old, living in Scottsdale, AZ.

    I”d love to make good close friends here.

    Thanks,
    Eva.

  4. hi there, my name is Tina.

    found this website and read some great discussion and feedback so decided to join

    i am happy to help others and offer advice where possible 🙂

  5. Hi I”m Dean from Encino, CA. I”m new member to this forum and I”m here to collect all the useful information on computer,internet web hosting ,reseller hosting and all. Can anyone here who can share more and more useful tips and useful information related to it.Than just connect with the forum and share more and more information.
    Thanks.
    Have a nice day.

  6. Thoughts lead on to purposes; purposes go forth in action; actions form habits; habits decide character; and character fixes our destiny.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: