3

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?

enzom83
  • 8,080
  • 10
  • 68
  • 114
  • Why not use the overload Dispose(WaitHandle) as shown in that link. I think this is valid implementation. stackoverflow.com/questions/4277044/… But if I am not sure and it is a comment. And why are you doing work (like backup) in the callback? – paparazzo Sep 17 '12 at 19:45
  • @Blam: I have a data structure which is updated by multiple threads, so I perform a periodic backup of this data structure. – enzom83 Sep 17 '12 at 19:58
  • 1
    This is fine. You don't need the Dispose(WaitHandle) overload, just plain Dispose() is good enough since you use a lock. – Hans Passant Sep 17 '12 at 20:31

1 Answers1

2

One of the approaches is to switch to System.Timers.Timer. I've blogged on it:

http://netpl.blogspot.com/2010/05/systemwindowsformstimer-vs.html

(the second part of the entry)

Wiktor Zychla
  • 47,367
  • 6
  • 74
  • 106
  • I don't know but it shouldn't matter as you stop/start the timer from within its own callback, not from just ANY thread. – Wiktor Zychla Sep 17 '12 at 22:27
  • However, to stop the timer from outside of the callback, I should take into account that two thread could race to access the `Timer` instance. Otherwise I invoke the `Dispose()` method from within the callback (no race condition), but I would need a shared `shutdown` flag: this flag would be set from outside of the callback... – enzom83 Sep 17 '12 at 22:53