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.
To end or quit the timer, one must use a cancel() function. Importing the threading class is necessary for one to use the threading class. The calling thread can be suspended for seconds using the function time. sleep(secs).
Timer is not thread-safe.
System.Threading. Timer Class. The Timer class (in the System. Threading namespace) is effective to periodically run a task on a separate thread. It provides a way to execute methods at specified intervals.
Timer raises the elapsed event, is it raised in an independent thread? Yes, they run in a different thread.
With this code
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 );
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.
As described in "Concurrent Programming on Windows":
Create a dummy class InvalidWaitHandle, inheriting from WaitHandle:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Threading;
namespace MyNameSpace
{
class InvalidWaitHandle : WaitHandle
{
}
}
Hence you can Dispose a System.Threading.Timer properly like this:
public static void DisposeTimer()
{
MyTimer.Dispose(new InvalidWaitHandle());
MyTimer = null;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With