The documentation about System.Threading.Timer
class states that:
Note that callbacks can occur after the Dispose() method overload has been called, because the timer queues callbacks for execution by thread pool threads.
This means that all the callbacks scheduled before calling the Dispose()
method must be completed. But how are callbacks scheduled? When is the next callback scheduled?
Suppose we create a new instance of the Timer
class with a finite period equal to P and a finite due time equal to D. The first execution of the callback will occur after D milliseconds have elapsed. Subsequent callbacks will be executed after P milliseconds from the start of the previous callback. Writing a code sample, you will see that if the callback execution time is greater than the specified period P, the timer queues callbacks.
Suppose that the callback should back up some data on file: the callback execution time is the backup execution time and depends on the amount of data that must be written to file, so the callback execution time may exceed the specified period. In this case it is more appropriate to schedule the next backup at the end of the current backup: for this purpose it is sufficient to disable the periodic behavior (by setting the period to Timeout.Infinite
) and to call the Change()
method at the end of the callback, as in the following sample code.
// some private fields
private readonly object syncRootForTimer = new object();
private Timer backupTimer = new Timer(BackupCallback, null, 10000, Timeout.Infinite);
private bool isTimerDisposed = false;
// it performs timer shutdown
public void Shutdown()
{
WaitHandle disposed = AutoResetEvent(false);
lock(syncRootForTimer)
{
backupTimer.Dispose(disposed);
isTimerDisposed = true;
}
WaitHandle.WaitAll(new WaitHandle[]{ disposed });
// ...
}
private void BackupCallback(object state)
{
// It performs the backup procedure...
// ...
lock(syncRootForTimer)
{
if (!isTimerDisposed)
backupTimer.Change(10000, Timeout.Infinite);
}
}
In this way, it seems that no callback is queued. I also noticed that invoking the Dispose(WaitHandle)
method immediately after the last schedule (that is after the last call to the Change()
method), this last schedule is not even executed and the timer is immediately disposed.
I think I can say that disabling the periodic behavior and changing the due time at the end of callback execution, the timer queues no callback. This should ensure that no callbacks can occur after the Dispose()
method has been called. Is my guess correct?