This post shows a simple and a more advanced way to wait for a specific time when executing code. The main purpose of this post is to use the functionality in a Windows Azure WorkerRole which is a class in a Windows Azure deployment that inherits RoleEntryPoint. The approach is to use System.Timers.Timer in conjunction with System.Threading.EventWaitHandle rather than the default template code that uses System.Threading.Thread.Sleep.

Comment unsuccessful. Please correct the errors below.

HowTo wait in a WorkerRole using Timer and EventWaitHandle over Thread.Sleep

This post shows a simple and a more advanced way to wait for a specific time when executing code. The main purpose of this post is to use the functionality in a Windows Azure WorkerRole which is a class in a Windows Azure deployment that inherits RoleEntryPoint. The approach is to use System.Timers.Timer in conjunction with System.Threading.EventWaitHandle rather than the default template code that uses System.Threading.Thread.Sleep.

The Waiter class below may be used for other general waiting purposes, not only for waiting in a WorkerRole.Run context.

Why would I want to do this? Well first of all I’m answering a question on Stack Overflow: How to use a Timer to replace Thread.Sleep(…) in an Azure Worker Role? Second I feel it’s really nice to use the Timer and EventWaitHandle classes to solve this problem rather than Thread.Sleep because it is hontestly just a little bit more the right tool for the job. The latter will waste a few cycles each time it waits while putting the Thread to sleep. Besides why do I want to put a thread to sleep just because I’m waiting?

First i will show you a simple solution to this problem.

Second I will input some thoughts on alternating the wait times in random and/or increased intervals and also discuss why you would want to do that?

Finally I will add a slightly more advanced scenario that solves the same problem by doing work while there is work to do and waiting for longer and longer intervals if there is no work to do.

Simple solution

For this nice simple solution, for the rest of the blog post about waiting and for your enjoyment, I figure some nice tunes are in order:

If my WorkerRole has the following layout:

public class WorkerRole : RoleEntryPoint
{
    Waiter waiter;

    public override bool OnStart()
    {
        // [...] 
waiter =
new Waiter(WorkerConfiguration.WaitInterval); return base.OnStart(); } public override void Run() { while (true) { DoWork(); waiter.Wait(); } } public void DoWork() { // [...] } }

(You can see that my worker will use some Waiter class to wait for a WorkerConfiguration.WaitInterval. The DoWork() method is left without implemention and any other OnStart() activities you might want to perform are removed for brevity.)

Then my Waiter class is implemented like this:

public class Waiter
{
    private readonly Timer timer;
    private readonly EventWaitHandle waitHandle;

    public Waiter(TimeSpan? interval = null)
    {
        waitHandle = new AutoResetEvent(false);
        timer = new Timer();
        timer.Elapsed += (sender, args) => waitHandle.Set();
        SetInterval(interval);
    }

    public TimeSpan Interval
    {
        set { timer.Interval = value.TotalMilliseconds; }
    }

    public void Wait(TimeSpan? newInterval = null)
    {
        SetInterval(newInterval);
        timer.Start();
        waitHandle.WaitOne();
        timer.Close();
        waitHandle.Reset();
    }

    private void SetInterval(TimeSpan? newInterval)
    {
        if (newInterval.HasValue)
        {
            Interval = newInterval.Value;
        }
    }
}

The combination here of a Timer to measure time and an EventWaitHandle to be able to wait for an event enables us to wrap away this functionality behind an easy to use .Wait()-method.

I think this solution is both fairly simple and rather elegant.

Why would you want to wait longer and longer times?

If you have a WorkerRole running in Windows Azure and there happens to be no work for it to do right now, you might not want to work for the exact same time all the time. The reason is that your scenario might allow you to back off waiting for longer and longer intervals if there is no work to do. Behind the scenes you can perhaps use a Windows Azure Storage Queue to poll for incoming work orders. If that is the case you don’t want to cause a lot of extra network traffic from your application to the Storage Service if there is no work to do.

For instance you might want to wait for 1, 2, 4, 8, 16, 42 seconds (see what I did there?) and then continue to wait for 42 seconds until there actually is a message added to the queue.

At the same time if the queue is full of work you may want to chisel away at the queue until it is empty and then start to wait for new work according to the above thoughts.

So… if this is the case – and it is a very common useful Windows Azure WorkerRole case – you could use the below WorkerRole skeleton and associated contracts.

More advanced solution

Here is a new version of a WorkerRole:

public class WorkerRole2 : RoleEntryPoint
{
    IWorkProvider workProvider;
    IWaitTimeManager waitTimeManager; 
    Waiter waiter;

    public override bool OnStart()
    {
        TimeSpan[] waitIntervals = new[] { 1, 2, 4, 8, 16, 42 }
.Select(i => TimeSpan.FromSeconds(i)).ToArray(); waiter = new Waiter(waitIntervals.First()); waitTimeManager = new SimpleWaitTimeManager(waitIntervals); workProvider = new NullWorkProvider();
return base.OnStart(); } public override void Run() {
        while (true)
        {
            Action work;
            if (workProvider.TryGetWork(out work))
            {
                waitTimeManager.Reset();
                do
                {
                    work();
                } while (workProvider.TryGetWork(out work));
            }

            var newInterval = waitTimeManager.NextInterval();
            waiter.Wait(newInterval);
        }
} }

The main points here are in the Run method.

  • As you can see work continues while there is work returned by the IWorkProvider.
  • When there is no work returned the Waiter waits for intervals decided by the IWaitTimeManager.
  • I have also implemented the contracts with two trivial implementations of each of my contracts SimpleWaitTimeManager and NullWorkProvider as simple trivial implementations.
    • SimpleWaitTimeManager is a simple implementation which will return the range of intervals provided in the constructor until the last value which will be returned over and over again until .Reset() is called.
    • NullWorkProvider will never return any work, always false.

For completeness here are my interfaces and classes used above:

public interface IWorkProvider
{
    bool TryGetWork(out Action work);
}

public class NullWorkProvider : IWorkProvider
{
    public bool TryGetWork(out Action work)
    {
        work = null;
        return false;
    }
}

public interface IWaitTimeManager
{
    void Reset();
    TimeSpan NextInterval();
}

public class StaticWaitTimeManager : IWaitTimeManager
{
    private readonly TimeSpan[] staticTimesToWait;
    private int index;

    public StaticWaitTimeManager(params TimeSpan[] staticTimesToWait)
    {
        this.staticTimesToWait = staticTimesToWait;
        index = 0;
    }

    public void Reset()
    {
        index = 0;
    }

    public TimeSpan NextInterval()
    {
        index = index == staticTimesToWait.Length – 1 ? index : ++index;
        return staticTimesToWait[index++];
    }
}

Summary

I think this turned out really nice and I believe I will begin using this template myself. Using Timer and EventWaitHandler feels cooler than the stubby Thread.Sleep. Hope you like it too!

HTH – Cheers,

Magnus

Windows Azure
Posted by: Magnus Mårtensson
Last revised: 2013-02-07 09:03 History

Comments

2013-02-11 09:17

Great post, had me thinking the whole time I drove into work. I ended up passing it off to one of the other developers and we had some ideas on how to use this pattern in reverse for something else (basically build a process that polls less frequently the more work you give it, so it batches more together each time, then goes back to smaller poll rates when less work is available).

It also made me finally write a post that's been in my drafts folder for a while on how to exit cleanly from a worker role: Azure Worker Role - Exiting Safely. Next up, combining your Waiter with the cancellation token to get safe exits and reduced poll rates :)

Login in to comment!