LP on .NET

July 7, 2009

Using Timers Instead of Managed Threads

Filed under: .NET,C#,Software Development — Larry Parker @ 9:55 pm

I have written several Windows services in .NET over the years, and they have all required some sort of housekeeping to occur in the background (e.g. archive or back up files every hour).

The approach I have used was to create a worker thread that sleeps and iterates until the desired time interval has elapsed.  For example:

public class Housekeeping
{
    public Housekeeping()
    {
        _thread = new Thread(HousekeepingThread);
    }

    private bool _active;
    private Thread _thread;

    public void Start()
    {
        _active = true;
        _thread.Start();
    }

    public void Stop()
    {
        _active = false;
        _thread.Join();
    }

    private void HousekeepingThread()
    {
        int iterations = 0;
        while (_active)
        {
            if (++iterations >= 720)
            {
                DoWork();
                iterations = 0;
            }

            Thread.Sleep(5000);
        }
    }

    private void DoWork()
    {
        // do some work
    }
}

After an hour (720 iterations X 5000 ms = 1 hour), the DoWork method is called and some work is performed.  The thread then resumes sleeping and iterating until the next hour interval elapses.

The reason for sleeping 5 seconds and iterating (instead of simply sleeping for one hour) is to make the thread more manageable.  If the calling program initiates the Stop method, it won’t have to wait more than 5 seconds to abort (although it could be a bit longer if DoWork is called).  This avoids having to abort the thread, which is not very graceful.

This approach is ok, and gives the illusion of some housekeeping occurring “every hour”, but there are a couple of problems with this that I never really considered until I recently read a discussion of timers (in the excellent book C# 3.0 in a Nutshell, by Joseph and Ben Albahari).

In Chapter 19,  Threading, the authors point out the exact technique that I have been using, and its shortcomings.  Nothing like seeing your approach used as an example of what not to do.  🙂

The problem with this pattern (perhaps it’s really an anti-pattern) is that a thread resource is tied up to do nothing but mainly sleep, and the DoWork method will not really be called every hour but will instead slip as time goes on (since DoWork takes some time to perform).

The solution to all of this is to use a timer.  There are several timers in .NET, but for this example we’ll rewrite our Housekeeping class to use System.Threading.Timer:

public class Housekeeping
{
    private Timer _timer;

    public void Start()
    {
        int hourMs = 1000 * 60 * 60;
        _timer = new Timer(DoWork, null, hourMs, hourMs);
    }

    public void Stop()
    {
        _timer.Dispose();
    }

    private void DoWork(Object stateInfo)
    {
        // do some work
    }
}

Not only is this simpler, but you don’t have to create your own thread.  The Timer class will use a thread from the thread pool to execute your callback delegate, so you won’t tie up a thread resource that does nothing but sleep!

Keep in mind that your callback may execute on different threads, so it must be thread safe.

But the best part of the Timer class is that your callback method gets executed on time.  In our example, it will fire exactly after one hour, and then fire exactly every hour thereafter.

Much easier than creating your own thread and managing the time interval yourself!

2 Comments »

  1. Hi Larry!
    In my new gig I work on a dozen Windows services and they all follow the “Larry-Pattern”. When the opportunity presents itself, I refactor the services to timers. Aside from the points you mentioned, you avoid an unnecessary context switch and will better utilize on-chip caching.

    Comment by David Chappelle — July 8, 2009 @ 10:01 am | Reply

  2. Hey Dave, good to hear from you!

    Glad that you’re cleaning up the code at your new job. And you can’t name this pattern after me because I learned it from someone else. 🙂

    Now that I know about timers, this other technique seems so brute force.

    Comment by Larry Parker — July 8, 2009 @ 7:30 pm | Reply


RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.