Timer kills task/job if it runs longer than timer interval - c#

I would to make a timer which behaviour is like this:
if processing time of task/job is less than timer interval, start timer in (timer.interval - processing time job/job)
if processing time of job/task is more than timer interval, start next job/task immediatly
Code below works but I would like to know why in the ElapsedEventHandler method job/task must be first done and then we can set new timer interval. Elapsed event of System.Timers.Timer is raised when interval has elapsed. With the option AutoReset = false we set that the Timer raises the Elapsed event only once, after the first Interval has elapsed. We have to then manually call Timer.Start() to start it again.
using System;
using System.Timers;
namespace TestTimer
{
class Program
{
private static Timer t;
private static double intervalMiliseconds;
static void Main(string[] args)
{
intervalMiliseconds = 5000; // 5 seconds
t = new Timer();
t.Interval = intervalMiliseconds;
t.AutoReset = false;
t.Elapsed += new ElapsedEventHandler(OnTimedEvent);
t.Start();
log("Timer started at " + DateTime.Now.ToString());
log("Interval is: " + defaultIntervalMiliseconds);
Console.ReadKey();
}
private static void log(string text)
{
Console.WriteLine(text + "\n");
}
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
// if t.Interval is set here thread just kills the job if it
// runs longer than interval
t.Interval = intervalMiliseconds;
log("ElapsedEvent triggered at " + DateTime.Now.ToString());
// job
DateTime startTime = DateTime.Now;
log("job started" );
System.Threading.Thread.Sleep(8000); // 8 sec
log("job ended" );
TimeSpan jobTime = DateTime.Now.Subtract(startTime);
log("job took " + jobTime.TotalSeconds + " seconds");
// if we set t.Interval here it works so first the job
// must be done and than we can set timer interval ? why ?
//t.Interval = intervalMiliseconds;
if (jobTime.TotalMilliseconds < t.Interval)
{
t.Interval = t.Interval - jobTime.TotalMilliseconds;
log("Job ended Earlier starting Event in: " + t.Interval);
}
else
{
t.Interval = 100;
log("Job overpass interval. Current time: " +
DateTime.Now.ToString());
}
t.AutoReset = false;
t.Start();
}
}
}
Result of this:
If we comment t.Interval at the start of method OnTimedEvent and uncomment t.Interval after the job has done everything works. Result of this:
Why we can not set timer interval at the start of method OnTimedEvent. If we do if the task/job runs longer than the timer interval, thread just kills the job. I would really appreciate if anyone has some ideas? Does this have to do with synhronization of thread with main thread (which timer runs)? When we call method OnTimedEvent the timer will not call that method again because it has AutoReset = false, what difference does it make where we set timer properties?

t.Interval = intervalMiliseconds;
That's indeed the troublemaker. It is pretty unintuitive behavior, one that the MSDN article for Timer.Interval specifically warns about in a Note:
If Enabled and AutoReset are both set to false, and the timer has previously been enabled, setting the Interval property causes the Elapsed event to be raised once, as if the Enabled property had been set to true. To set the interval without raising the event, you can temporarily set the AutoReset property to true.
It's a fairly silly hack but does work. Just delaying assigning the value is certainly the better way to do it. Doing it early doesn't buy you anything, other than trouble, the timer isn't going tick anyway since you've got AutoReset = false.
System.Threading.Timer is the better timer class with many fewer quirks. It for one doesn't swallow exceptions without any diagnostic in the callback method. Which your code is quite sensitive to, the timer will just stop ticking since the exception bypasses the t.Start() call and you'll have no idea why. Strongly recommended.

!! No and never Timer kill task/job if it runs longer than timer interval !!
if processing time of task/job is less than timer interval,
after timer interval/span.
if processing time of job/task is more than timer interval,
start next job/task after timer interval/span into a new Thread.
so for minimizing idle time you should keep timer interval small.
In System.Timers.Timer class internally already Threading Implemented. so don`t need to implements threading.

Related

Capture Countdown or Tick Event - Threading.Timer C#

I have developed a program using Threading.Timer to call a method after 30 seconds.
The method is getting called, it's working but I want to get the event so that I can know how many seconds are left out of 30 sec.
I need a kind of tick event which gets fired every seconds. So that I can show the remaining seconds as well. This is my code:
Timer timerObj;
timerObj = new Timer(UpdateUI, null, Timeout.Infinite, Timeout.Infinite);
timerObj.Change(30000, Timeout.Infinite);
public void UpdateUI(object state)
{
ShowQR= false;
}
How can I get countdown even ?
This is how you'd go about implementing Evk's suggestion:
Timer timerObj;
Stopwatch stopwatch;
...
timerObj = new Timer(UpdateUI, null, Timeout.Infinite, Timeout.Infinite);
timerObj.Change(1000, Timeout.Infinite);
stopwatch = Stopwatch.StartNew();
public void UpdateUI(object state)
{
if (stopwatch.Elapsed < TimeSpan.FromSeconds(30))
{
timerObj.Change(1000, Timeout.Infinite);
return;
}
stopwatch.Restart();
ShowQR= false;
}
Note how you start the Stopwatch at the same time as you call timerObj.Change() to start the timer running.
Also note how the timer is set to fire every second rather than every 30s.
Then in the timer handler, you simply check the elapsed time on the Stopwatch - if it's less that 30s then just return; otherwise, do the thing you want to do every 30s and (IMPORTANT!) restart the Stopwatch.

Timer won't start after stop

In my application I'm using two Timer, each Timer use a BackgroundWorker. Here the declaration:
DispatcherTimer timer1 = new DispatcherTimer();
DispatcherTimer timer2 = new DispatcherTimer();
BackgroundWorker worker1 = new BackgroundWorker();
BackgroundWorker worker2= new BackgroundWorker();
I using timer1 for perform an heavy method with a BackgroundWorker and timer2 for execute another BackgroundWorker that check the content of a file.
In this way I assign the event to BackgroundWorkers:
worker1.DoWork += worker_DoWork;
worker1.RunWorkerCompleted += worker_RunWorkerCompleted;
worker1.WorkerSupportsCancellation = true;
worker2.DoWork += worker_DoWork2;
worker2.RunWorkerCompleted += worker_RunWorkerCompleted2;
worker2.WorkerSupportsCancellation = true;
Now timer1 have a range of 15 minutes so the BackgroundWorker execute the heavy method each 15 minutes. And timer2 have a range of 1 second. With the timer1 all working good, but the problems are coming when I've added the timer2.
As I said before this timer allow me to start a method that read a file through the worker2, this file have a property, if this property change I need to perform some special activity. Until here no problem.
What I did is the following:
//This method is called by MainWindow
public ReadFile()
{
//before this I already assigned to timer1 the tick event and start
timer2.Tick -= new EventHandler(Event_Tick);
timer2.Tick += new EventHandler(Event_Tick);
timer2.Interval = new TimeSpan(0, 0, 1);
timer2.Start();
}
This is the Tick event associated to timer2
private void Event_Tick(object sender, EventArgs e)
{
if (!worker1.IsBusy) //I skip the reading, worker1 is busy
{
timer1.Stop(); //stop the first timer
worker2.RunWorkerAsync();
}
else
{
Console.WriteLine("worker1 is busy!");
}
}
I don't need to add here the DoWork, is just a parsing of a file, very useless for the question. When worker2 complete the task I did this:
private void worker_RunWorkerCompleted2(object sender, RunWorkerCompletedEventArgs e)
{
timer1.Start();
ReadFile();
}
How you can see I start the timer1 again, and execute again the ReadFile method. Now if timer1 has reached the interval, so 15 minutes has passed, should execute the timer1.Tick += new EventHandler(Heavy_Tick); that execute the DoWork to worker1. But the timer1 never start.
I can't figure out to this, what am I doing wrong?
Now I get it!
You want to execute worker1 every 15 minutes and worker2 every second but only when worker1 is not busy. Your problem is this here:
if (!worker1.IsBusy) //I skip the reading, worker1 is busy
{
timer1.Stop(); //stop the first timer
worker2.RunWorkerAsync();
}
and this:
public ReadFile()
{
//before this I already assigned to timer1 the tick event and start
timer2.Tick -= new EventHandler(Event_Tick);
timer2.Tick += new EventHandler(Event_Tick);
timer2.Interval = new TimeSpan(0, 0, 1);
timer2.Start();
}
Set both timer intervals and tick event handlers during startup, e.g. Form_Load()or at the beginning of Main(). Start them there too. You should not have to stop any timer at all!
By setting the interval, all you have to do is handle the Tick() event. Remove your .Start() and Stop() calls from your WorkerCompletedand Tick methods and you should do fine.
So a lot could be going on here but you should make sure that:
You timer isn't storing it's old progress and you are checking for a certain length of time before stopping. This will automatically cause the timer to stop when restarting.
The timer.stop() function is not disposing your object to an un-restart-able state.
You aren't accessing the timer variable through some pointer that is maintain a stopped value. (Unlikely but annoying when it happens)
I'd personally consider just pausing the timer and resetting the progress, instead of fully stopping it since this is causing issues.

prevent this unsafe cross threading

I have this code:
private void Form1_Load(object sender, EventArgs e)
{
/*start update timer*/
System.Timers.Timer updateticker = new System.Timers.Timer();
updateticker.Elapsed += new ElapsedEventHandler(update_overload);
//10 minute ticker
updateticker.Interval = 600000;
//30 sec ticker
updateticker.Interval = 30000;
updateticker.Enabled = true;
System.Timers.Timer guiTimer = new System.Timers.Timer();
guiTimer.Elapsed += new ElapsedEventHandler(idle_display);
//1 minute ticker
guiTimer.Interval = 60000;
//30 sec ticker
//updateticker.Interval = 30000;
guiTimer.Enabled = true;
}
//run front end idle timer
public void idle_display(object source, ElapsedEventArgs e)
{
if (minutes_left > 0) {
minutes_left = minutes_left - 1;
}
lbl_dyn_status.Text = "Time until next automatic update: "+ minutes_left + " minutes.";
}
Visual studio is flagging the final line of the second function as unsafe cross threading. Can anyone suggest how I rewrite this to solve the issue?
Cheers
Use System.Windows.Forms.Timer instead of System.Timers.Timer
or use SynchronizingObject property of Timer.
When SynchronizingObject is null, the method that handles the Elapsed event is called on a thread from the system-thread pool. For more information on system-thread pools, see ThreadPool.
When the Elapsed event is handled by a visual Windows Forms component, such as a button, accessing the component through the system-thread pool might result in an exception or just might not work. Avoid this effect by setting SynchronizingObject to a Windows Forms component, which causes the method that handles the Elapsed event to be called on the same thread that the component was created on.
As others suggested, use System.Windows.Forms.Timer. Unlike System.Threading.Timer, which operates on Thread Pool threads, it guarantees you that the tick event is invoked on the UI thread.
The UI thread is the only thread allows to modify the UI. This is why you get an exception trying to write lbl_dyn_status.Text.
Use System.Windows.Forms.Timer timer instead of System.Timers.Timer.
Change the handler to
public void IdleDisplay(object source, ElapsedEventArgs e)
{
if (lbl_dyn_status.InvokeRequired)
{
this.Invoke(IdleDisplay)
}
else
{
if (minutes_left > 0)
{
minutes_left = minutes_left - 1;
}
lbl_dyn_status.Text = string.Format(
"Time until next automatic update: {0} minutes.",
minutes_left);
}
}
This way allows you to use the thread free System.Threading.Timer but checks for a cross-thread call in the handler. If detected the call is invoked on the main GUI thread, via the Form class.
This is described on MSDN in more detail here.
More generally, you should not use timers to count time like this. The more thread bound your timer is, the more it is likely to diverge from real elapsed time. You can use your timer to schedule an update of your clock but, you should calculate elapsed time since some fixed point rather than using an iterative counter.

C#, System.Timers.Timer, run every 15min in sync with system clock

How do I get System.Timers.Timer to trigger Elapsed events every 15 mins in sync with the system clock? In other words, I want it to trigger exactly at xx:00, xx:15, xx:30, xx:45 (where xx means any hour)
You could let it elapse every second and check whether the current time is 00, 15, 30 or 45 and only then forward the event.
A first idea would be:
private static System.Timers.Timer aTimer;
private static System.DateTime _last;
public static void Main()
{
aTimer = new System.Timers.Timer(10000);
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Interval = 1000;
aTimer.Enabled = true;
Console.WriteLine("Press the Enter key to exit the program.");
Console.ReadLine();
}
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
DateTime time =
new DateTime( 1,1,1, DateTime.Now.Hours, DateTime.Now.Minute );
if( time.Minute==0 ||time.Minute==15 || time.Minute==30 || time.Minute==45 )
{
// Avoid multiple notifications for the same quarter.
if ( _last==DateTime.MinValue || _last!=time )
{
_last = time;
// Do further processing.
doProcessing();
}
}
}
(Example based on this MSDN documentation)
When starting the program, or changing the event times that will be triggered, load the event times into memory (to keep from reading this data from the hard drive every second.) Then set up a timer to fire every 1 second. A timer set to fire every 1 second is very little overhead on the processor. Set one up and open task manager and you will not even notice the processor running any more than before the timer was running. Then put a check in the timer event to check if it is time to fire an event.
use Quartz.net. Then you can use regex to define the interval.

How to reset a timer in C#?

There are three Timer classes that I am aware of, System.Threading.Timer, System.Timers.Timer, and System.Windows.Forms.Timer, but none of these have a .Reset() function which would reset the current elapsed time to 0.
Is there a BCL class that has this functionality? Is there a non-hack way of doing it? (I thought perhaps changing the time limit on it might reset it) Thought on how hard it would be to reimplement a Timer class that had this functionality, or how to do it reliably with one of the BCL classes?
I always do ...
myTimer.Stop();
myTimer.Start();
... is that a hack? :)
Per comment, on Threading.Timer, it's the Change method ...
dueTime Type: System.Int32 The
amount of time to delay before the
invoking the callback method specified
when the Timer was constructed, in
milliseconds. Specify
Timeout.Infinite to prevent the
timer from restarting. Specify zero
(0) to restart the timer immediately.
All the timers have the equivalent of Start() and Stop() methods, except System.Threading.Timer.
So an extension method such as...
public static void Reset(this Timer timer)
{
timer.Stop();
timer.Start();
}
...is one way to go about it.
For System.Timers.Timer, according to MSDN documentation, http://msdn.microsoft.com/en-us/library/system.timers.timer.enabled.aspx:
If the interval is set after the Timer has started, the count is
reset. For example, if you set the interval to 5 seconds and then set
the Enabled property to true, the count starts at the time Enabled is
set. If you reset the interval to 10 seconds when count is 3 seconds,
the Elapsed event is raised for the first time 13 seconds after
Enabled was set to true.
So,
const double TIMEOUT = 5000; // milliseconds
aTimer = new System.Timers.Timer(TIMEOUT);
aTimer.Start(); // timer start running
:
:
aTimer.Interval = TIMEOUT; // restart the timer
You could write an extension method called Reset(), which
calls Stop()-Start() for Timers.Timer and Forms.Timer
calls Change for Threading.Timer
I just assigned a new value to the timer:
mytimer.Change(10000, 0); // reset to 10 seconds
It works fine for me.
at the top of the code define the timer: System.Threading.Timer myTimer;
if (!active)
myTimer = new Timer(new TimerCallback(TimerProc));
myTimer.Change(10000, 0);
active = true;
private void TimerProc(object state)
{
// The state object is the Timer object.
var t = (Timer)state;
t.Dispose();
Console.WriteLine("The timer callback executes.");
active = false;
// Action to do when timer is back to zero
}
For a Timer (System.Windows.Forms.Timer).
The .Stop, then .Start methods worked as a reset.
You can do timer.Interval = timer.Interval
I do the following.
Disposing the timer and initializing it again.
But this will erase any event you attached to this timer.
timer.Dispose();
timer = new System.Timers.Timer();
Other alternative way to reset the windows.timer is using the counter, as follows:
int timerCtr = 0;
Timer mTimer;
private void ResetTimer() => timerCtr = 0;
private void mTimer_Tick()
{
timerCtr++;
// Perform task
}
So if you intend to repeat every 1 second, you can set the timer interval at 100ms, and test the counter to 10 cycles.
This is suitable if the timer should wait for some processes those may be ended at the different time span.
i do this
//Restart the timer
queueTimer.Enabled = true;

Categories

Resources