I have a Windows Service implemented in C# that needs to do some work every so often. I’ve implemented this using a System.Threading.Timer 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:
const int tickInterval = 1000; // one second
timer = new Timer( state => {
// 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();
The problem is that I get an ObjectDisposedException from timer.Change on the callback thread while waitHandle.WaitOne is blocking. What am I doing wrong?
The documentation for the Dispose overload I’m using says:
The timer is not disposed until all currently queued callbacks have completed.
Edit: It appears that this statement from the documentation may be incorrect. Can someone verify?
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.
With this code
it is almost certain that you will Dispose the timer while it is sleeping.
You will have to safeguard the code after Sleep() to detect a Disposed timer. Since there is no IsDisposed property a quick and dirty
static bool stopping = false;might do the trick.