Friday, 16 August 2013

Quick replacement for Thread.Sleep

Ever needed to replace Thread.Sleep?

 Since you know you should be using it!

Or you haven't worked that out yet, or not been in that situation, but you've wanted to wake from a sleep call (Then you're in a situation where you shouldn't be using Thread.Sleep, and just not realized it yet)

Or your lazy and used Thread.Sleep knowing better because most of the code you could nick for the web has been too overbearing.

I've been in all three camps, and got tired of it and wrote this concise class to add to my toolbelt (someone else has probably done the same, I just didn't find it).

It will allow you the effect of sleep while still allowing callbacks to fire in the thread! Which is rather important when for

Example


You have a windows service which has to execute a task every x minutes.

As a minimum you'd have something to this affect

        public void DoWorkEveryXMinutes()
        {
            while (!stopWork)
            {
                Work();
                Thread.Sleep(TimeSpan.FromMinutes(5));
            }
        }

        public void Stop()
        {
            stopWork = true;
        }


So what happens when you want to stop working? You have to wait at least 5 minutes for the Thread.Sleep to complete, never mind the "work" task.

Hail the "Sleeper" class

    public class Sleeper:IDisposable
    {
        private readonly AutoResetEvent wake;
        private readonly Timer timer;

        public Sleeper()
        {
            wake = new AutoResetEvent(false);
            timer = new Timer(x => wake.Set());
        }

        public void Sleep(TimeSpan timeSpan)
        {
            timer.Change(timeSpan, TimeSpan.FromMilliseconds(-1));
            wake.WaitOne();
            timer.Change(Timeout.Infinite, Timeout.Infinite);
        }

        public void Wake()
        {
            wake.Set();
        }

        public void Dispose()
        {
            timer.Dispose();
        }
    }


All you need to do now is create a instance of Sleeper to use (I'm still thinking about adapting it to work as Static) Replace your "Thread.Sleep" with "mySleeper.Sleep" Optionally add a Wake call "mySleeper.Wake"
        
        public void DoWorkEveryXMinutes()
        {
            while (!stopWork)
            {
                Work();
                //Thread.Sleep(TimeSpan.FromMinutes(5));
                mySleeper.Sleep(TimeSpan.FromMinutes(5));
            }
        }

        public void Stop()
        {
            stopWork = true;
            mySleeper.Wake();
        }
        

For simplicity and bravity I left out the manual reset event I've used in my sample code, which ensure tasks are completed before Stop returns giving the impression everything has stopped, which is not necessarily the case.

Check out the sample code here for complete but brief code. It makes use of Nuget package restorer, but you'll need to rename the file \.nuget\nuget.xex to .exe.
This also makes a useful basic sample of a Windows service using Topshelf. It sets you up with self installation and VS debugging, all things I used to set up myself, but now get in a supported package :D  ... so if your not using it, get on it. (Don't understand why VS services projects don't set up this way and requires 3rd party package)

No comments:

Post a Comment