Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Waiting for a timer elapsed event to complete before application/service closes/stops

Tags:

c#

timer

Summary: Within a Windows service & Console Application I am calling a common library that contains a Timer that periodically triggers an action that takes around 30 seconds to complete. This works fine, however...

When a service stop or application exit is called and the timer is in the ElapsedEventHandler I need the service stop/application exit to wait until the event handler has completed.

I have implemented this functionality by having a Boolean InEvent property that is checked when the timer stop method is called.

While this is functional, the question is: Is this the best way to go about doing this? Is there an alternative approach that may serve this purpose better?

The other issue is that I need to avoid the service stop request failing with a "Service failed to respond to stop request"

This is my implementation

public sealed class TimedProcess : IDisposable
{
    static TimedProcess singletonInstance;
    bool InEvent;
    Timer processTimer;

    private TimedProcess()
    {
    }

    public static TimedProcess Instance
    {
        get
        {
            if (singletonInstance == null)
            {
                singletonInstance = new TimedProcess();
            }

            return singletonInstance;
        }
    }

    public void Start(double interval)
    {
        this.processTimer = new Timer();
        this.processTimer.AutoReset = false;
        this.processTimer.Interval = interval;
        this.processTimer.Elapsed += new ElapsedEventHandler(this.processTimer_Elapsed);
        this.processTimer.Enabled = true;
    }

    public void Stop()
    {
        if (processTimer != null)
        {
            while (InEvent)
            {
            }

            processTimer.Stop();
        }
    }

    void processTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        try
        {
            InEvent = true;
            // Do something here that takes ~30 seconds
        }
        catch
        {
        }
        finally
        {
            InEvent = false;
            processTimer.Enabled = true;
        }
    }

    public void Dispose()
    {
        if (processTimer != null)
        {
            Stop();
            processTimer.Dispose();
        }
    }
}

And this is how it is called in the service OnStart / console application main:

TimedProcess.Instance.Start(1000);

This is how it is called in service OnStop and application main (pending keypress):

TimedProcess.Instance.Stop();
like image 523
MrEyes Avatar asked Aug 12 '11 16:08

MrEyes


People also ask

How do I stop a timer elapsed event?

It will fire at the elapsed time. To avoid this happening set Timer. AutoReset to false and start the timer back in the elapsed handler if you need one. Setting AutoReset false makes timer to fire only once, so in order to get timer fired on interval manually start timer again.

What is timer Elapsed event in Windows service?

Elapsed event every two seconds (2000 milliseconds), sets up an event handler for the event, and starts the timer. The event handler displays the value of the ElapsedEventArgs. SignalTime property each time it is raised.

What does timer elapsed mean?

Elapsed time is the amount of time that passes from the start of an event to its finish. In simplest terms, elapsed time is how much time goes by from one time (say 3:35pm) to another (6:20pm). An important tool that goes hand and hand with elapsed time is the clock.


1 Answers

Probably the easiest and most reliable way is to use a Monitor. Create an object that the main program and the timer callback can access:

private object _timerLock = new object();

Your main program tries to lock that before shutting down:

// wait for timer process to stop
Monitor.Enter(_timerLock);
// do shutdown tasks here

And your timer callback locks it, too:

void processTimer_Elapsed(object sender, ElapsedEventArgs e)
{
    if (!Monitor.TryEnter(_timerLock))
    {
        // something has the lock. Probably shutting down.
        return;
    }
    try
    {
        // Do something here that takes ~30 seconds
    }
    finally
    {
        Monitor.Exit(_timerLock);
    }
}

The main program should never release the lock once it's obtained it.

If you want the main program to go ahead and shut down after some period of time, regardless of whether it's obtained the lock, use Monitor.TryEnter. For example, this will wait 15 seconds.

bool gotLock = Monitor.TryEnter(_timerLock, TimeSpan.FromSeconds(15));

The return value is true if it was able to obtain the lock.

By the way, I strongly suggest that you use System.Threading.Timer rather than System.Timers.Timer. The latter squashes exceptions, which can end up hiding bugs. If an exception occurs in your Elapsed event, it will never escape, meaning that you never know about it. See my blog post for more information.

like image 188
Jim Mischel Avatar answered Nov 15 '22 13:11

Jim Mischel