Partager via


Periodic Execution in .NET

Frequently in my role I see code that is doing something like the following :-

After Time (x) Do (y). Repeat ad infinitum. No user interface.

I’ve deliberately put this in nothing approximating code as that’s what this article is about. Now, before reading the rest of this article ask yourself this – how would you do the above?

I’m prepared to bet you came up with one of the following answers :-

Option 1 : The dedicated thread
 public static void Main()
{
    Thread t = new Thread(new ThreadStart(ThreadFunc));
    t.IsBackground = true;
    t.Name = "Worker";
    t.Start();

    // Other code here omitted for clarity
}

private static void ThreadFunc()
{
    while(true)
    {
        Thread.Sleep(x);
        y();
    }
}

The problem here is I cannot shut down the worker thread cleanly as it’s in a tight loop. And I’m running up a thread to have it most probably sleep for most of its lifetime.

Option 2 : A Timer

OK, a Timer you say – but which one? A System.Timers.Timer, a System.Threading.Timer or a System.Windows.Forms.Timer? Confused – you should be. And then you could also look at the System.Windows.Threading.DispatcherTimer too.

If you were choosing one of these to do the job I’m after you would probably pick System.Threading.Timer as it’s pretty light weight and does just what I need. MSDN suggests the following information about when to use these timer objects :-

  • The Windows timer (System.Windows.Forms.Timer) is designed for a single-threaded environment where UI threads are used to perform processing. The accuracy of Windows timers is limited to 55 milliseconds. These traditional timers require that the user code have a UI message pump available and always operate from the same thread, or marshal the call onto another thread. For a COM component, this would be detrimental to performance.
  • The server-based timer (System.Timers.Timer) is designed for use with worker threads in a multi-threaded environment. Because they use a different architecture, server-based timers have the potential to be much more accurate than Windows timers. Server timers can move among threads to handle the raised events.
  • The thread timer (System.Threading.Timer) is useful in scenarios where messages are not pumped on the thread. For example, the Windows-based timer relies on operating-system timer support, and if you are not pumping messages on the thread, the event associated with your timer will not occur. The thread timer is more useful in this case.
Option 3 : Some hand-baked approach

Maybe you maintain a list of all timers, so that you can use a dedicated thread to service all of these, and all you do is sleep until the next event is due, process that event, and then calculate the next time you need to wake up.

Is there another way?

I’d advocate none of the above. Heresy? – Maybe. But then again maybe you don’t know about the other way of doing this, all supported in the .NET framework – and been there since version 1.0 hit the streets some 8 years ago or so. Arise sir API, and make yourself known to us :-

 ThreadPool.RegisterWaitForSingleObject ( ... ) 

It might not be immediately clear what RegisterWaitForSingleObject is up to, or how you could use this to solve the initial problem I posed, but all will be revealed.

The documentation for this method states “Registers a delegate to wait for a WaitHandle, specifying something as the timeout”. So how does that apply to our situation then?

Option 4 : RegisterWaitForSingleObject

The important thing to note is that this method can repeatedly call your delegate. And it does this as long as you like. In addition you also get the opportunity to alert this function (i.e. manually force it to run) which could be useful. Say for example you have a background thread that periodically checks for new messages – but you also want to be able to kick this processing off manually too in response to some user input. RegisterWaitForSingleObject is perfect in this case.

So lets get to some code. The first example calls my periodic function every 1000ms.

 class Program
{
    static void Main(string[] args)
    {
        AutoResetEvent exit = new AutoResetEvent(false);

        RegisteredWaitHandle h = ThreadPool.RegisterWaitForSingleObject(exit, new WaitOrTimerCallback(ThreadFunc), 
                                                                        null, 1000, false);

        Console.WriteLine("Running - press enter to stop");
        Console.ReadLine();
    }

    static void ThreadFunc(object state, bool timedOut)
    {
        Console.WriteLine("Thread running - {0}", timedOut ? "Timeout" : "Signalled");
    }
}

 

So here I have constructed an AutoResetEvent, then called RegisterWaitForSingleObject. My ThreadFunc corresponds to the WaitOrTimer delegate definition by taking a state object (this is the null passed into the RWFSO call) and a flag indicating that a timeout occurred, rather than the waitable object being set.

If you run this you’ll see the ‘Thread running – Timeout’ message show up roughly every second.

Now the WaitHandle you use has a bearing on your function being called – or rather the number of times your delegate is called. I’ve used an AutoResetEvent which is effectively reset once something has read it. So, if I were to call Set() on the event then the delegate would be called once, and it would output ‘Thread running – Signalled’.

If I were to use a ManualResetEvent and call Set() on it, I’d then get loads of ‘Thread running – Signalled’ messages, until I manually reset the event. It’s an important point to note – if the event stays signalled then the callback method will be called repeatedly.

The other important thing to note is that if you signal your event then the thread function will be called at least once. This is good – as you can use this behaviour to your advantage.

Say you’re writing a Windows Service which periodically wakes up and does some work. You’ve probably got some code in the OnStop() service method to wait around and close things down gracefully. How about using an event to notify your code that it’s time to stop? This would be an ideal way to utilise RWFSO and the code is presented below :-

 public void OnStart()
{
    _stop.Reset();
    _registeredWait = ThreadPool.RegisterWaitForSingleObject(_stop, 
        new WaitOrTimerCallback(PeriodicProcess), null, 5000, false);
}

public void OnStop()
{
    _stop.Set();
}

private void PeriodicProcess(object state, bool timeout)
{
    if (timeout)
    {
        // Periodic processing here
    }
    else
        // Stop any more events coming along
        _registeredWait.Unregister(null);
}

private ManualResetEvent _stop = new ManualResetEvent(false);
private RegisteredWaitHandle _registeredWait;

So in the OnStart method I setup the ManualResetEvent which is used to indicate the service is stopping, and then register a wait.

In the callback function I check if there was a timeout in which case the periodic process can continue, otherwise if the event object has been signalled I can unregister the wait which will stop any further invocations of the PeriodicProcess function. The parameter to RegisteredWaitHandle.Unregister is another wait handle that you may wish to signal to indicate that the un-registration succeeded (thanks to an eagle eyed reader who spotted this, I've updated the sample code).

One last thing

With the code above I’ve had to store the RegisteredWaitHandle so that I can call Unregister later. Another method you can employ (which doesn’t require this storage) is to pass ‘true’ as the last parameter to RegisterWaitForSingleObject. This gives you a one-shot timer, which may sound useless if what you want is periodic execution, but you can then use the function as follows :-

 public void OnStart()
{
    _stop.Reset();
    ThreadPool.RegisterWaitForSingleObject(_stop, new WaitOrTimerCallback(PeriodicProcess), null, 5000, true);
}

public void OnStop()
{
    _stop.Set();
}

private void PeriodicProcess(object state, bool timeout)
{
    if (timeout)
    {
        // Periodic processing here

        // Then queue another wait
        ThreadPool.RegisterWaitForSingleObject(_stop, new WaitOrTimerCallback(PeriodicProcess), null, 5000, true);
    }
}

private ManualResetEvent _stop = new ManualResetEvent(false);

Here I’m simply making another call to RegisterWaitForSingleObject within the PeriodicProcess method. Note that using this method may be easier from the perspective of having one less instance variable, but it does mean that your episodic execution is now not necessarily running evenly. With the old method, we could get a callback every (say) 5 seconds, whether the previous one had finished or not. With the above solution we do one set of periodic processing, then wait 5 seconds, and then do another set. Obviously this second way of processing may be advantageous to you – that’s your call, I’m just here to point it out.

Hopefully this has helped you understand how to use RegisterWaitForSingleObject, and maybe provided you with another way to execute episodic functions.

Comments

  • Anonymous
    December 22, 2008
    The comment has been removed

  • Anonymous
    March 07, 2011
    Think there's a bug in your code: _registeredWait.Unregister(_stop); should be _registeredWait.Unregister(null); I made this mistake many times, mis-understanding what that parameter is actually for, but (as I understand it) it's not the waithandle you originally registered the wait against, it's a mechanism for syncronising the unregistration of the wait with the code executing as a result of the wait callback. The doco is near-non-existant here, but Joe Duffy's Concurrent Programming book talks about this, which is where I realised I'd got it wrong from.

  • Anonymous
    March 14, 2011
    Piers7 - thanks, and well spotted! I have updated the code appropriately and also added in a bit of explanation below. Cheers, Morgan

  • Anonymous
    November 09, 2012
    Hello Morgan, first of all thanks for this post! I have a question: what if "periodic treatment here", inside PeriodicProcess procedure,  was a quite long task and, in the meantime, OnStop was triggered? Would be PeriodicProcess code running "twice", the first waiting, for example, for the end of a subroutine call (eg, file processing) and the second due to OnStop? TIA, Vik

  • Anonymous
    January 02, 2013
    The comment has been removed

  • Anonymous
    August 13, 2014
    Thanks for publishing this. I couldn't make any of the timers work reliably. I was using TASK to start 4 different Socket connections. I needed them all to complete at the same time. Then repeat every 2 seconds using your idea. It's been working now for over 2 days without any problems. Thank you again.