Hello,
I am using a LargeIntervalTimer for an event that should occur every 30 seconds, or when a user requests it be performed right now (think of a POP mail checker). The event may take longer than 30 seconds, and should be run again 30 seconds after it completes not 30 seconds after it starts (otherwise it can run continually, eating up the device's battery).
I put together a test class with these member variables. It uses log4net to log its progress.
int sleepTime = 45000;
int timerInterval = 30000;
LargeIntervalTimer timer = new LargeIntervalTimer();
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(Form1));
This code in the Form's Load event sets up the event handler:
// Set up the timer
log.Debug("Running with timer_Tick1");
timer.Tick += new EventHandler(timer_Tick1);
And these methods set the timer and simulate the work:
private void setTimer()
{
log.Debug("Set timer for " + timerInterval + " ms");
timer.Enabled = false;
timer.Interval = TimeSpan.FromMilliseconds(timerInterval);
timer.OneShot = true;
timer.Enabled = true;
}
void doWork()
{
log.Debug("doing work for " + sleepTime + " ms");
Thread.Sleep(sleepTime);
log.Debug("done with work");
}
I initially implemented this by simply having the Tick event handler disable the timer (so if the user requested the action a timer won't start a second action when it expires), do the work, then set up the timer again. This ran the first time, but not after that. Here is the code:
void timer_Tick1(object sender, EventArgs e)
{
log.Debug("timer ticked");
timer.Enabled = false;
doWork();
setTimer();
}
My next implementation ran the work and set the timer in a seperate thread. Its success seemed to depend on scheduling; if the Tick handler completed before the thread started it would work OK, otherwise it would not. When it failed it would sometimes not run at all, and sometimes the event would run immediately when setTimer() was run. For the test version I have added a Thread.sleep() call to cause the thread to run before the Tick handler exits, demonstrating the problem:
void timer_Tick6(object sender, EventArgs e)
{
log.Debug("timer ticked");
ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object arg)
{
timer.Enabled = false;
doWork();
setTimer();
}));
Thread.Sleep(2000);
}
If I take the opposite approach, making the thread sleep to ensure the Tick handler finishes first, things seem to work as they should, though it is a bit of a hack and not guaranteed to work all the time:
void timer_Tick7(object sender, EventArgs e)
{
log.Debug("timer ticked");
ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object arg)
{
Thread.Sleep(2000);
timer.Enabled = false;
doWork();
setTimer();
}));
}
So, my question is: is this expected behavior, or a bug? Are there restrictions on when LargeIntervalTimer objects can be updated, and are they documented anywhere? Is it better to create a new LargeIntervalTimer every time, or will that consume a lot of resources? And is there a better way of doing this that I haven't thought of?
Thanks!
----Scott.