I have a System.Threading.Timer which fires frequently (let's say every second for simplicity), in the CallBack I need to call an Action (which is passed in via the constructor, so sits in another class) within which I do some processing (let's say it takes 2+ seconds), how would I prevent my processing logic from being called multiple times? It seems that a lock() doesn't work within the Action call? I am using .net 3.5.
public TestOperation(Action callBackMethod)
{
this.timer = new System.Threading.Timer(timer_Elapsed, callbackMethod, timerInterval, Timeout.Infinite);
}
private void timer_Elapsed(object state)
{
Action callback = (Action) state;
if (callback != null)
{
callback();
}
}
// example of the callback, in another class.
private void callBackMethod()
{
// How can I stop this from running every 1 second? Lock() doesn't seem to work here
Thread.Sleep(2000);
}
Thanks!
There's nothing pretty about having to solve this problem. Note that using lock is a very bad idea, it will make your threadpool explode when the callback consistently takes too much time. This happens easily when the machine gets loaded. Using Monitor.TryEnter() is the safe alternative. Definitely not pretty either, you'll arbitrarily lose callbacks.
It gets a heckofalot easier if you simply set the period argument to 0. So that the timer can tick only once. Now you automatically have a hard guarantee that the callback cannot be re-entered. All you have to do is call Change() at the end of the method to restart the timer. It is up to you to use a fixed value or calculate a new dueTime value based on the actual amount of time that expired, either are reasonable choices.
You could do something like this and avoid timers altogether.
void Main()
{
RunPeriodicAsync();
}
async Task RunPeriodicAsync()
{
while(true)
{
await Task.Delay(someTimeSpan);
DoTheThing();
if(!goAgain)break;
}
}
or if you need to support cancellation:
void Main()
{
var cts=new CancellationTokenSource();
RunPeriodicAsync(cts.Token);
//sometime later
cts.Cancel();
}
async Task RunPeriodicAsync(CancellationToken ct)
{
while(!ct.IsCancellationRequested)
{
await Task.Delay(1000);
DoTheWork();
}
}
Without async/await you could:
System.Threading.Timer timer;
void Main()
{
RunActionAfter(() => DoTheWork(), 2000);
}
void RunActionAfter(Action action, int period)
{
//Timeout.Infinite means the timer runs only once.
timer = new Timer(_ => action(), null, 2000, Timeout.Infinite);
}
void DoTheWork()
{
Console.WriteLine("!!!");
//then maybe
RunActionAfter(() => DoTheWork(),2000);
}
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