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