How to use a Timer to replace Thread.Sleep(…) in a

2019-02-27 03:33发布

问题:

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!

回答1:

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;
        }
    }
}