Search code examples
c#multithreadingeventstimerwindows-services

Timer Elapsed event one by one in Windows Service


I create windows service in C#. I use timer elapsed method to perform some CRUD operation with database. for example

tmrAPACSEvent = new Timer(10000);
tmrAPACSEvent.Elapsed += new ElapsedEventHandler(tmrAPACSEvent_Elapsed);
tmrAPACSEvent.Enabled = true;

I create this timer in OnStart method and Event is

private void tmrAPACSEvent_Elapsed(object sender, ElapsedEventArgs e)
{
    Library.WriteErrorLog("tmrAPACSEvent ticked and some job has been done successfully");

    try
    {
        var UnProcessedEvents = BLCustomService.GetCustomEvents();

        if (UnProcessedEvents != null)
        {
            foreach (EventData eventData in UnProcessedEvents)
            {
                Library.WriteErrorLog(BLCustomService.ChangeEventStatus(eventData).ToString());
                Library.WriteErrorLog(eventData.EventId.ToString());
                var ed = new EventData()
                {
                    dtRealDateTime = eventData.dtRealDateTime,
                    dwCardNumber = eventData.dwCardNumber,
                    strHolderName = eventData.strHolderName,
                    strInitObjName = eventData.strInitObjName
                };

                ed = BLCustomService.AddEvent(ed);
            }
            Library.WriteErrorLog("Events added");
        }
    }
    catch (Exception exception)
    {
        Library.WriteErrorLog("Error 96: " + exception.StackTrace);
    }
}

When I start the service the event doing parallel and I get more than one data in DB for corresponding "Event"s. I know the operation time bigger than timer elapse time. But I want every thread must wait completions other thread that start before its.

--Possible Solution-- I create global static bool variable nad assign its true. in My Event add it to if condition and after its I assign it false until end of foreach loop.

  private static bool adminForElapsedEvent = true;
    private void tmrAPACSEvent_Elapsed(object sender, ElapsedEventArgs e)
    {
        Library.WriteErrorLog("tmrAPACSEvent ticked and some job has been done successfully");

        try
        {
            var UnProcessedEvents = BLCustomService.GetCustomEvents();

            //Library.WriteErrorLog("tmrAPACSEvent ticked and some job has been done successfully" + UnProcessedEvents.Count);
            if (UnProcessedEvents != null || adminForElapsedEvent)
            {
                adminForElapsedEvent = false;

                // doing some wordks
                adminForElapsedEvent = true;
            }
        }
        catch (Exception exception)
        {
            Library.WriteErrorLog("Error 96: " + exception.StackTrace);
        }
    }

First time this method works, but after few minutes Elapse event runs again, and repeat insertion.


Solution

  • First thing you can try: set your tmrAPACSEvent.Enabled = false instead of adminForElapsedEvent = false;. Then you won't even need the global variable.

    This is not fool-proof as you are running into race conditions. So the next suggestion is to put a lock around your code. But don't use a normal lock, use a Mutex which I find gives better resource access control. So your code will now look like this:

    // Create a new Mutex. The creating thread does not own the mutex.
    private static Mutex mux = new Mutex();
    
    private void tmrAPACSEvent_Elapsed(object sender, ElapsedEventArgs e)
    {
        Library.WriteErrorLog("tmrAPACSEvent ticked and some job has been done successfully");
    
        try
        {
            var UnProcessedEvents = BLCustomService.GetCustomEvents();
    
            if (UnProcessedEvents != null)
            {
                tmrAPACSEvent.Enabled = false;
    
                // Wait until it is safe to enter, and do not enter if the request times out.
                if (mux.WaitOne(7000))
                {
                    // do work
    
                    // note this is inside the mutex so that the timer is guaranteed enabled, even for just a moment
                    tmrAPACSEvent.Enabled = true;
                }
            }
        }
        catch (Exception exception)
        {
            Library.WriteErrorLog("Error 96: " + exception.StackTrace);
        }
    }