Search code examples
c#multithreadingtimer

Timer Method does not stop execution though it finishes its task


I have a windows service that should poll a database for every 5 seconds for the new entries and update the log file.

I am using System.Timers.Timer class to call my Database method and calling Autocancellation for every 5 seconds.

 protected override void OnStart(string[] args)
 {
     try
     {
            ServiceLogFile("Service is started at " + DateTime.Now);
            timer.Elapsed += new ElapsedEventHandler(Autocancellation);
            timer.Interval = Int32.Parse(ConfigurationManager.AppSettings["tracktime"]); //number in miliseconds 
            timer.Enabled = true;
      }
      catch(Exception ex)
      {
           ServiceLogFile("Error in {OnStart} :" + ex.ToString());
      }
  }

  public void Autocancellation(object source, ElapsedEventArgs e)
  {
        try
        {
            lock (this)
            {

                //Database accesss
                //select statement 

                //Adding to Data table 
                if(dt.Rows.Count>0)
                {
                    //Update Statements 
                }
                else
                {
                     ServiceLogFile("There is no orders in the table Thread id :" +Thread.CurrentThread.ManagedThreadId);
                 }
              }
       }
   }

Autocancellation method will be having database access and update statements.

Expected Workflow -

It should update all the entries in the database and return back to the onStart method and poll for every 5 seconds.

Issue -

Whenever it completes updating the rows and if it has time (like if it finishes the updation statement in 2 mins at the last Iteration) and remaining 3 mins it will print "There is no orders in the table". I am not able to stop printing this until it finishes whole its timer.

I tried stopping the timer - But this leads to it will never poll the database again from onStart method.

Here is the sample of logs

When updation starts -

Service is stopped at 8/4/2019 1:34:15 PM
Service is started at 8/4/2019 1:34:28 PM
Database check :8/4/2019 1:34:33 PM
{INFO} Cancelled Order {AccessionID} : A1
{INFO} Cancelled Order {AccessionID} : A2
{INFO} Cancelled Order {AccessionID} : A3

End of Iteration -

{INFO} Cancelled Order {AccessionID} : A49997
{INFO} Cancelled Order {AccessionID} : A49998
{INFO} Cancelled Order {AccessionID} : A49999
{INFO} Cancelled Order {AccessionID} : A50000
Database check :8/4/2019 1:51:53 PM
There is no orders in the table Thread id :4
Database check :8/4/2019 1:51:53 PM
There is no orders in the table Thread id :3
Database check :8/4/2019 1:51:53 PM
There is no orders in the table Thread id :7

If we can see the above logs its looping in the same method to print no records. I tried with killing thread and return statements. Nothing worked.

After this Iteration time (5 seconds) This will be returned to the onStart and polls for 5 seconds correctly and logs are perfect -

Database check :8/4/2019 1:52:04 PM
There is no orders in the table Thread id :96
Database check :8/4/2019 1:52:09 PM
There is no orders in the table Thread id :97
Database check :8/4/2019 1:52:14 PM

Help in stopping the timer for the running instance and it should poll back as expected.


Solution

  • I would set the AutoReset property to False and re-start the timer after there are no more records. The reason for this is your Autocancellation might be called multiple times from different threads while you are busy processing the records.

    From MSDN:

    Gets or sets a Boolean indicating whether the Timer should raise the Elapsed event only once (false) or repeatedly (true).

    protected override void OnStart(string[] args)
     {
         try
         {
                ServiceLogFile("Service is started at " + DateTime.Now);
                timer.AutoReset = false;
                timer.Elapsed += new ElapsedEventHandler(Autocancellation);
                timer.Interval = Int32.Parse(ConfigurationManager.AppSettings["tracktime"]); //number in miliseconds 
                timer.Enabled = true;
                timer.Start();
          }
          catch(Exception ex)
          {
               ServiceLogFile("Error in {OnStart} :" + ex.ToString());
          }
      }
    
    public void Autocancellation(object source, ElapsedEventArgs e)
    {
    
        try
        {
            lock (this)
            {
    
                //Database accesss
                //select statement 
    
                //Adding to Data table 
                if(dt.Rows.Count>0)
                {
                    //Update Statements 
                }
                else
                {
    
                     ServiceLogFile("There is no orders in the table Thread id :" +Thread.CurrentThread.ManagedThreadId);
                 }
              }
       }
       finally
       {
    // in anycase, start the timer again. In this pattern, you will not get
    // any calls until the processing is finished.    
                timer.Start();
           }
        }