I have a System.Threading.Timer that calls its appropriate event handler (callback) every 10 ms. The method itself is not reentrant and can sometimes take way longer than 10 ms. Thus, I want to stop the timer during method execution.
Code:
private Timer _creatorTimer;
// BackgroundWorker's work
private void CreatorWork(object sender, DoWorkEventArgs e) {
_creatorTimer = new Timer(CreatorLoop, null, 0, 10);
// some other code that worker is doing while the timer is active
// ...
// ...
}
private void CreatorLoop(object state) {
// Stop timer (prevent reentering)
_creatorTimer.Change(Timeout.Infinite, 0);
/*
... Work here
*/
// Reenable timer
_creatorTimer.Change(10, 0);
}
MSDN states that the callback method is called (every time the timer fires) in separate thread from the thread pool. That means that if I stop the timer the first thing in method it still doesn't neccessarily prevent the timer to fire and run another instance of the method before the first one had a chance to stop the timer.
Should maybe the timer (or even the non-reentrant method itself) be locked? What is the right way to prevent timer from firing during execution of its callback (and non-reentrant) method?
I've had similar situation with a System.Timers.Timer, where the elapsed event is executed from a threadpool and needs to be reentrant.
I used this method to get around the issue:
Depending on what you're doing you may want to consider a System.Timers.Timer, here's a nice summary from MSDN
I do it with Interlocked that provides atomic operations, and by CompareExchange ensures that only one thread at a time enters the critical section:
A couple possible solutions:
You may be able to manage option #2 without disposing/creating a new object by using the
Change()
method of the original timer object, but I'm not sure what the behavior is exactly of callingChange()
with a new start timeout after the first timeout has expired. That would be worth a test or two.Edit:
I did the test - manipulating the timer as a restartable one-shot seems to work perfectly, and it's much simpler than the other methods. Here's some sample code based on yours as a starting point (a few details may have changed to get it to compile on my machine):
You could let the timer continue firing the callback method but wrap your non-reentrant code in a Monitor.TryEnter/Exit. No need to stop/restart the timer in that case; overlapping calls will not acquire the lock and return immediately.