How to block a timer while processing the elapsed event?

14,074

Solution 1

You could set AutoReset to false, then explicitly reset the timer after you are done handling it. Of course, how you handle it really depends on how you expect the timer to operate. Doing it this way would allow your timer to drift away from the actual specified interval (as would stopping and restarting). Your mechanism would allow each interval to fire and be handled but it may result in a backlog of unhandled events that are handled now where near the expiration of the timer that cause the handler to be invoked.

timer.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds;
timer.Elapsed += Timer_OnElapsed;
timer.AutoReset = false;
timer.Start();


public void Timer_OnElapsed(object sender, ElapsedEventArgs e)
{
    if (!found)
    {
      found = LookForItWhichMightTakeALongTime();
    }
    timer.Start();
}

Solution 2

I usually stop the timer while processing it, enter a try/finally block, and resume the timer when done.

Solution 3

If LookForItWhichMightTakeALongTime() is going to take a long time, I would suggest not using a System.Windows.Forms.Timer because doing so will lock up your UI thread and the user may kill your application thinking that it has frozen.

What you could use is a BackgroundWorker (along with a Timer if so desired).

public class MyForm : Form
{
  private BackgroundWorker backgroundWorker = new BackgroundWorker();

  public MyForm()
  {
    InitializeComponents();
    backgroundWorker.DoWork += backgroundWorker_DoWork;
    backgroundWorker.RunWorkerCompleted +=
                                backgroundWorker_RunWorkerCompleted;
    backgroundWorker.RunWorkerAsync();
  }

  private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
  {
    e.Result = LookForItWhichMightTakeALongTime();
  }

  private void backgroundWorker_RunWorkerCompleted(object sender,
                                             RunWorkerCompletedEventArgs e)
  {
    found = e.Result as MyClass;
  }
}

And you can call RunWorkerAsync() from anywhere you want to, even from a Timer if you want. And just make sure to check if the BackgroundWorker is running already since calling RunWorkerAsync() when it's running will throw an exception.

private void timer_Tick(object sender, EventArgs e)
{
  if (!backgroundWorker.IsBusy)
    backgroundWorker.RunWorkerAsync();
}
Share:
14,074
Michael Hedgpeth
Author by

Michael Hedgpeth

DevOps/Automation architect focused on chef

Updated on June 05, 2022

Comments

  • Michael Hedgpeth
    Michael Hedgpeth almost 2 years

    I have a timer that needs to not process its elapsed event handler at the same time. But processing one Elapsed event may interfere with others. I implemented the below solution, but something feels wrong; it seems like either I should be using the timer differently or using another object within the threading space. The timer seemed to fit best because I do need to periodically check for a status, but sometimes checking will take longer than my interval. Is this the best way to approach this?

    // member variable
    private static readonly object timerLock = new object();
    private bool found = false;
    
    
    // elsewhere
    timer.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds;
    timer.Elapsed = Timer_OnElapsed;
    timer.Start();
    
    
    public void Timer_OnElapsed(object sender, ElapsedEventArgs e)
    {
      lock(timerLock)
      {
        if (!found)
        {
          found = LookForItWhichMightTakeALongTime();
        }
      }
    }
    
  • Peter Lillevold
    Peter Lillevold about 15 years
    You should always dispose Timer instances when you're done with'em
  • Samuel
    Samuel about 15 years
    AutoReset and Elapsed are for System.Threading.Timer which is not safe for UI work.
  • tvanfosson
    tvanfosson about 15 years
    @Samuel -- Elapsed is the event that the OP is using. Presumably this isn't a WinForms app.
  • Andre Loker
    Andre Loker over 7 years
    The timer will keep firing the callback method as long as the garbage collector has not cleaned it up. This may cause unexpected, non-deterministic effects.