title: How do I gracefully stop a System.Threading.Timer?
body: <p>I have a Windows Service implemented in C# that needs to do some work every so often. I've implemented this using a <code>System.Threading.Timer</code> with a callback method that is responsible for scheduling the next callback. I am having trouble gracefully stopping (i.e. disposing) the timer. Here's some simplified code you can run in a console app that illustrates my problem:</p>
  
  <pre><code>const int tickInterval = 1000; // one second
  
  timer = new Timer( state =&gt; {
                         // simulate some work that takes ten seconds
                         Thread.Sleep( tickInterval * 10 );
  
                         // when the work is done, schedule the next callback in one second
                         timer.Change( tickInterval, Timeout.Infinite );
                     },
                     null,
                     tickInterval, // first callback in one second
                     Timeout.Infinite );
  
  // simulate the Windows Service happily running for a while before the user tells it to stop
  Thread.Sleep( tickInterval * 3 );
  
  // try to gracefully dispose the timer while a callback is in progress
  var waitHandle = new ManualResetEvent( false );
  timer.Dispose( waitHandle );
  waitHandle.WaitOne();
  </code></pre>
  
  <p>The problem is that I get an <code>ObjectDisposedException</code> from <code>timer.Change</code> on the callback thread while <code>waitHandle.WaitOne</code> is blocking. What am I doing wrong?</p>
  
  <p><a href="http://msdn.microsoft.com/en-us/library/b97tkt95.aspx" rel="noreferrer">The documentation</a> for the <code>Dispose</code> overload I'm using says:</p>
  
  <blockquote>
    <p>The timer is not disposed until all currently queued callbacks have completed.</p>
  </blockquote>
  
  <p><strong>Edit: It appears that this statement from the documentation may be incorrect. Can someone verify?</strong></p>
  
  <p>I know that I could work around the problem by adding some signaling between the callback and the disposal code as Henk Holterman suggested below, but I don't want to do this unless absolutely necessary.</p>