2

There's plenty of examples of people saying to use a Timer instead of Thread.Sleep(...) in an Azure Worker Role. No probs with that.

What I'm struggling to understand is how to code this.

Currently, I have the following (pseduo code)

_timer.Elapsed += (sender, args) => DoWork();

public override void Run()
{
    while(true)
    {
        DoWork();
    }
}

public void DoWork()
{
    try
    {
        _timer.Stop();

        // Now - do stuff ....

     }
     catch(....) { ... }

     _timer.Start()
}

And what happens, is that the code enters the DoWork() method once and DoesStuff(tm).. fine .. starts the timer (say .. with a 30 second interval) and then exits that method.

Then, it returns back to the main Run() method .. which is in that loop. So it immediately comes back around and enters the DoWork() method again .. instead of waiting for the timer to fire it off.

So I'm not sure how to replace any Thread.Sleep(...) with Timers.

Any clues?

Clarification

I do not want to exit the Run() method :) I'm very happy to keep looping forever. What I'm stuck with, is replacing the standard Thread.Sleep(...) call (which blocks the thread) and replace that with a Timer, which most people suggest.

Update

Please do not link or suggest that I should use cancelSource.Token.WaitHandle.WaitOne(); as a solution. That is not what I'm trying to achieve here. Please note the post title!

Community
  • 1
  • 1
Pure.Krome
  • 84,693
  • 113
  • 396
  • 647
  • How about taking DoWork() outside of infinite loop and put good old Thread.Sleep(10000) in that loop. The idea is to initialize the timer just once which you can do it in the Run() method. – Gaurav Mantri Feb 05 '13 at 13:03
  • Did you read the opening line about replacing `Thread.Sleep(..)` because it's NotGood(tm)? – Pure.Krome Feb 05 '13 at 13:04
  • If I understand correctly, the idea is that the WorkerRole never comes out of Run() method otherwise your worker role will restart (http://stackoverflow.com/questions/9561746/using-thread-sleep-or-timer-in-azure-worker-role-in-net). I guess what I'm not understanding is why is this a bad idea? Are you looking for the functionality where you want to schedule "DoStuff()" to get called every 30 seconds? – Gaurav Mantri Feb 05 '13 at 13:25
  • ah Sorry. OK, i don't want to exit the Run() method (so you're correct). But I don't want to use `Thread.Sleep(..)` to 'pause' the worker (for a while). Instead, I want to use a Timer. – Pure.Krome Feb 05 '13 at 14:17
  • 2
    The first search result on this problem gave another SO question with an elegant enough answer where you simply kick off the timer in the "do stuff" section, flagged as duplicate question. http://stackoverflow.com/questions/9561746/ – Oskar Duveborn Feb 05 '13 at 14:23
  • @OskarDuveborn If the solution you are referring to is the `ancelSource.Token.WaitHandle.WaitOne(); solution, that's actually not what I'm after :( I'm after a solution that leverage's `Timer.Start()` and `Timer.Stop()` (as mentioned in the Subject and Body of the message). – Pure.Krome Feb 05 '13 at 23:17
  • @OskarDuveborn I don't agree that it's a duplicate. The answer doesn't git the question here. – Phill Feb 07 '13 at 10:44

1 Answers1

6

I figure that if you want to solve this situation the way you outline here you will need a WaitHandle AND a Timer.

The short answer is here below. The long answer became a blog post: HowTo wait in a WorkerRole using Timer and EventWaitHandle over Thread.Sleep

I used an EventWaitHandle along with the Timer and came up with this solution:

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()
    {
        // [...]
    }
}

And here is the waiter class:

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;
        }
    }
}
noopman
  • 660
  • 1
  • 4
  • 15