After reading SO extensively, I understand that Thread.Sleep
is a bad idea. Instead, the general consensus is that server-side tasks should use a Timer
, a threadpool
, or maybe use a Join()
.
One article mentions difficulties in disposing the timer.
Another article mentions using waitOne
Question
What is the right approach to use when launching a long running task that will repeat every 30 seconds, 1 minute, or 5 minutes? The constraint is that IF the previous run of that task is longer than the interval (32 seconds, or 7 minutes) then I want the option to either kill that previous instance, or not execute a new instance.
A potential gotcha is that I intend to use impersonation on these threads using either WindowsImpersionationContext, P/InvokeLogonUserEX, or DCOMShim as needed.
I'm not sure what approach to take, and why.
Possible Answer 1
This example appears to be straightforward, with minimal code clutter
// initially set to a "non-signaled" state, ie will block
// if inspected
private readonly AutoResetEvent _isStopping = new AutoResetEvent(false);
/// <summary>
/// from...
/// https://stackoverflow.com/questions/2822441/system-timers-timer-threading-timer-vs-thread-with-whileloop-thread-sleep-for-p/2822506#2822506
/// </summary>
public void SampleDelay1()
{
TimeSpan waitInterval = TimeSpan.FromMilliseconds(1000);
// will block for 'waitInterval', unless another thread,
// say a thread requesting termination, wakes you up. if
// no one signals you, WaitOne returns false, otherwise
// if someone signals WaitOne returns true
for (; !_isStopping.WaitOne(waitInterval); )
{
// do your thang!
}
}
Possible Answer 2
This example offers similar functionality, but uses anonymous types that may not be permitted in companies that don't allow for that in their coding standard.
/// <summary>
/// Disposable Timer instance from
/// https://stackoverflow.com/questions/391621/compare-using-thread-sleep-and-timer-for-delayed-execution
/// </summary>
class TimerStackOverFlow
{
// Created by Roy Feintuch 2009
// Basically we wrap a timer object in order to send itself as a context in order
// to dispose it after the cb invocation finished. This solves the problem of timer
// being GCed because going out of context
public static void DoOneTime(ThreadStart cb, TimeSpan dueTime)
{
var td = new TimerDisposer();
// Is the next object System.Timers, or System.Threading
var timer = new Timer(myTdToKill =>
{
try
{
cb();
}
catch (Exception ex)
{
Trace.WriteLine(string.Format("[DoOneTime] Error occured while invoking delegate. {0}", ex), "[OneTimer]");
}
finally
{
((TimerDisposer)myTdToKill).InternalTimer.Dispose();
}
},
td, dueTime, TimeSpan.FromMilliseconds(-1));
td.InternalTimer = timer;
}
}
class TimerDisposer
{
public Timer InternalTimer { get; set; }
}