user1290171
user1290171

Reputation: 31

Alternative to Thread.Sleep

Working on a windows service, which has to process request in every predefined interval of time. Thread.Sleep does the work perfectly fine but problem with this is when service is invoked to be stopped, service freeze if thread is in sleep mode. I have read about the alternative approach like Timer, but problem with that is after that defined interval new thread is getting started. Is there a better way to achieve same result and not run in to issue.

Upvotes: 3

Views: 2861

Answers (5)

jgauffin
jgauffin

Reputation: 101150

I usually use the following pattern:

public class MyJob
{
    System.Threading.Timer _timer;
    bool _isStopped;

    public void MyJob()
    {
        _timer = new Timer(OnWork, null, TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(-1));
    }

    private void OnWork(object state)
    {
        //[.. do the actual work here ..]

        if (!_isStopped)
            _timer.Change(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(-1));
    }

    public void Stop()
    {
        _isStopped = true;
        _timer.Change(TimeSpan.FromSeconds(-1), TimeSpan.FromSeconds(-1));
    }

    public void Start()
    {
        _isStopped = false;
        _timer.Change(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(-1));
    }
}

Key points:

  • Only using the initial interval gives you full control of when the timer is started again (i.e. the work time is not counted in the timer interval)
  • Changing the timer to -1 seconds pauses it until changed again

It should therefore work with all your requirements.

Upvotes: 2

Brian Gideon
Brian Gideon

Reputation: 48949

For what it is worth most of the blocking calls in the .NET BCL will respond to Thread.Interrupt. That is, they will not wait for the full amount of time specified when called and instead return immediately. However, I would avoid using this method and instead use a single ManualResetEvent to perform both the idle waiting and the shutdown signal. It would look like this.

public class MyServer : ServiceBase
{
  private ManualResetEvent shutdown = new ManualResetEvent(false);

  protected override void OnStart(string[] args)
  {
    new Thread(
      () =>
      {
        while (!shutdown.WaitOne(YourInterval))
        {
          // Do work here.
        }
      }).Start();
  }

  protected override void OnStop()
  {
    shutdown.Set();
  }
}

Upvotes: 0

Matt Davis
Matt Davis

Reputation: 46044

What you're looking for is the ability to respond to the notification of two different events - (1) when the timer elapses and (2) when the service is stopped. @Anurag Ranhjan is on the right track with WaitHandle, but you have two events, not one. To properly handle this, do the following.

First, define the two events you care about using ManualResetEvent. You can use AutoResetEvent if you prefer; I just prefer resetting the events manually.

using System.Threading;
ManualResetEvent shutdownEvent = new ManualResetEvent();
ManualResetEvent elapsedEvent = new ManualResetEvent();

You need to trigger these events when they occur. For the shutdownEvent, it's easy. In the OnStop callback of your Windows service, just set the event.

protected override void OnStop
{
    shutdownEvent.Set();
}

For the elapsedEvent, you could do this a couple different ways. You could create a background thread, i.e., the ThreadPool, that uses Thread.Sleep. When the thread wakes up, set the elapsedEvent and go back to sleep. Since it's a background thread, it won't hang your service when it shuts down. The alternative, as you've already suggested, is to use a timer. This is how I do it.

using System.Timers;
Timer timer = new Timer();
timer.Interval  = 5000;   // in milliseconds
timer.Elapsed  += delegate { elapsedEvent.Set(); };
timer.AutoReset = false;  // again, I prefer manual control
timer.Start();

Now that you've got events being set properly, put them in a WaitHandle array.

WaitHandle[] handles = new WaitHandle[]
{
    shutdownEvent,
    elapsedEvent
};

Instead of the WaitHandle.WaitOne method, use the WaitHandle.WaitAny method inside a while loop, like this.

while (!shutdownEvent.WaitOne())
{
    switch (WaitHandle.WaitAny(handles))
    {
        case 0:  // The shutdownEvent was triggered!
            break;
        case 1:  // The elapsedEvent was triggered!
            Process();             // do your processing here
            elapsedEvent.Reset();  // reset the event manually
            timer.Start();         // restart the timer manually
            break;
        default:
            throw new Exception("unexpected switch case");
    }
}

I've condensed this example from production code in my project. I know this mechanism works, but I may have missed something in the writeup. Let me know if you have any questions.

Upvotes: 3

Drona
Drona

Reputation: 7234

Use a Timer to add commands/tasks including the task for shutdown to a blocking queue. Make your service thread to wait for tasks on the blocking queue and execute them when available. The timer thread will keep adding the tasks to the queue periodically.

Upvotes: 1

Raj Ranjhan
Raj Ranjhan

Reputation: 3917

You can use WaitHandle.WaitOne instead. You can wait for closing event to trigger or timeout that you are specifying in predefined interval of time.

 static AutoResetEvent seviceStopRequested = new AutoResetEvent(false);
 ....
 ((AutoResetEvent)stateInfo).WaitOne([timeout], false)

Then when Service stop is invoked, you can just trigger the event

 seviceStopRequested .Set();

Upvotes: 2

Related Questions