Firing an event on fixed intervals - c#

I have a scenario where I want to track if my Console application is alive or dead (due to exceptions etc..).
While the console application is performing its processing I want to have a mechanism where it has a timer which keeps on calling a reporting method after every N minutes, this method being called updates a time stamp in the DB.
So :
1 - Console application starts
2 - Reports time stamp
3 - Keeps on doing its task (while in parallel it reports again after N seconds)
I know I can spawn a thread and make it sleep and wake up again after the "N" minutes but how reliable is that since threads might not fire until resources are free and the "N" minutes might not be valid anymore since the thread will fire on its own time.
How can I make sure the reporting is done in parallel while the processing is going on?

Rather than create your own thread use a built in Timer:
private DateTime startTime;
private System.Threading.Timer theTimer;
Start the timer:
this.startTime = DateTime.Now;
TimerCallback callback = TimerTick;
this.theTimer = new System.Threading.Timer(callback, null, 0, 1000);
The callback method:
void TimerTick(object stateInfo)
{
TimeSpan currentTime = DateTime.Now - startTime;
// Do your stuff here.
}
This creates a timer with no delay that fires every second.
MSDN page
The first integer is the dueTime amount of time to delay before the callback method is invoked. Specifying zero starts the timer immediately.
The second integer is the period - i.e. the time interval between invocations of the callback mathod.
You need to either make sure that your processing in TimerTick takes less than the period you have specified or ensure that it it re-entrant - i.e it can cope with being called before the previous invocation has completed. How you do that - or indeed whether you can do that - depends on your application.

Related

Timer takes more time

Scenario:
In a Winform application(C#), I have a Datagridview in which I have to display 4 countdown timers in the format "mm:ss".
Time Interval must be 1000ms.
I was counting the timer down in the elapsed event of system.timers.timer.
On all 4 timers I'm starting to countdown from 2 mins (02:00).
Issue:
It takes more time(125 seconds) than 2 mins, to reach 00:00.
Similarly for 4 mins it takes 7-10 more(247 -250) seconds to reach 00:00
Timers on systems are somewhat of an inaccurate beast. Better systems generally provide better timers but even the best system has slippage.
You also need to remember that your process isn't going to be in "full control" 100% of the time, it will at times be pushed into the background and have to share the processor with other applications so whilst it's does its best to keep track of the time.
What you probably want is a High Precision Timer (aka stopwatch) in C#. Have a look at This thread and This article on selecting timer mechanisms for some more information.
If you need time resolution of that type (i.e. actual clock or countdown clock), you should use the real-time clock.
You still use a timer with sub-second resolution to fire frequently enough for display purpose, but you don't add up those times, you use the real-time clock to get the real elapsed time (DateTime.Now - startTime).
First off you should run this little test and see the drift in near real-time. I lose a 1 second after 72 timer elapsed events (this is with very little actual work).
using (var timer = new Timer(1000))
{
var start = DateTime.Now;
var i = 0;
timer.Elapsed += (sender, args) => Console.WriteLine("{0} -> {1}", ++i, (DateTime.Now - start).TotalMilliseconds);
timer.Start();
Thread.Sleep(130*1000);
}
I'm not sure how precise your app needs to be but you can get "good enough" by using the delta between the start time & now and subtracting that from your initial value and killing the timer at zero. You will lose seconds with this approach and there's a reasonable chance that lost second could happen # 0:00 causing a -0:01 tick, which you will need to handle.
var countdownSeconds = 120;
var startedAt = DateTime.Now;
var timer = new Timer(1000);
timer.Elapsed += (sender, args) => Display(countdownSeconds - (int)((DateTime.Now - startedAt).TotalSeconds));
timer.Start();
//- be sure to dispose the timer

Timer lagging behind

I want to have a running timer in my app which displays the seconds elapsed upto 2 decimal places e.g. 2.31 seconds. I'm using System.Threading.Timer for this and I'm setting the Timer object to call refreshTimeBox() function every 10 milliseconds (because I want the seconds upto 2 decimal places). But, the timer lags behind. And as the time passed increases, it lags more and more behind. I'm guessing its because refreshTimeBox() is taking too long to complete. But, I also tried calling refreshTimeBox() every 100 milliseconds instead. The timer still lags. The only difference is that the lag becomes noticeable after a longer time in this case than in the case where I use 10 milliseconds as interval. I have the following code to initialize the timer:
timer = new Timer(refreshTimeBox, null, 0, 10);
The following is the code for refreshTimeBox:
public void refreshTimeBox(object param)
{
time += 0.01f;
Dispatcher.BeginInvoke(WriteTimeBox);
}
The following is the code for WriteTimeBox:
public void WriteTimeBox()
{
TimeBar.Text = time.ToString("0.00");
}
time is the variable which stores the time elapsed and TimeBar is the text box which is being updated.
I want as accurate a timer as possible. Please help me with this. Thanks.
If you want to display the amount of time that has elapsed since a particular event in the past, then the best way to do that is to store the time at "time zero", e.g. startTime, and then, in your timer event, computer the time that has elapsed by subtracting startTime from the current time, and displaying the difference.
You can't rely on timer events being delivered at exact intervals. This isn't a realtime system. Added to which, you should be aware that BeginInvoke is likely requesting that the method call occur on a different thread to the current one, and that different thread may not be able to dispatch that method call at the current time.
You could also use a stopwatch
System.Diagnostics.Stopwatch sw=new System.Diagnostics.Stopwatch();
sw.Start();
And in the update function
TimeBar.Text = sw.ElapsedMilliseconds;
Then you don't have to worry about UTC issues
Timers always have the possibility of lagging. What you need to do is store when you started, then compare it to the current time.
private DateTime startTime;
public void startTimer() {
startTime = DateTime.Now;
}
public void refreshTimeBox(object param)
{
Dispatcher.BeginInvoke(WriteTimeBox);
}
public void WriteTimeBox()
{
TimeSpan ts = DateTime.Now - startTime;
// See http://msdn.microsoft.com/en-us/library/1ecy8h51.aspx for TimeSpan formatting.
TimeBar.Text = ts.time.ToString("s.ff");
}

C# .NET 2 Threading.Timer - time drifting

I have a dll consumed by a service. Its basic job is to run every X minutes and perform some system checks.
In my dll I have a top level class that declares a System.threading.timer and a Timercallback.
The constructor for the class initialises the timerCallback with my thread function.
In my "Onstart" handler I initialise the timer with the timercallback and set the next time to fire and interval time. In my case its every 10 minutes.
Usually in these 10 minute checks there is nothing to do but the service is forced to do something at least once every day at a set time.
My problem: I am finding that during testing, the time the daily check is carried out every day is slowly drifitng away from the desired start time of 8.30. e.g. over about 20 odd days my time has drifted from 08.30 to 08.31.35. It drifts about 4 - 6 seconds every day.
My question: does anyone know why the time is drifting like this and how can I make it stick to its allotted time?
thanks
The time "drifts" because the timer is simply not that precise. If you need to run your code as closely as possible to a certain interval, you can do something like this:
public void MyTimerCallback(object something) {
var now = DateTime.UtcNow;
var shouldProbablyHaveRun = new DateTime(
now.Year, now.Month, now.Day,
now.Hour, now.Minute - (now.Minute % 10), 0);
var nextRun = shouldProbablyHaveRun.AddMinutes(10.0);
// Do stuff here!
var diff = nextRun - DateTime.UtcNow;
timer.Change(diff, new TimeSpan(-1));
}
...assuming you are using a System.Threading.Timer instance. Modify the example if you are using any other type of timer (there are several!).
Why not check every minute if the action needs to be performed?
ie:
if (DateTime.Now.Minute % 10) == 0
it takes a finite amount of time to do the operations you are doing in your timer method handler, so it makes sense that it's not going to happen every 10 minutes to the second, especially if you are scheduling the next wakeup after doing your checks and such. if you are already checking anyway for answering is it time to do this, you should make your timer fire more frequently to satisfy the resolution you need and trust your check of when it should execute something to make sure that it does. you probably need some sort of persistence to make sure it doesn't execute twice (if that is important) in case there is a shut down/restart and the state of knowing whether it has already run is not still in memory.
Here is my take:
while ((DateTime.Now - lastRunTime).TotalSeconds < 600)
CurrentThread.Sleep(1000);
or just register a windows timer and execute in response to the event/callback
public static void Main()
{
System.Timers.Timer aTimer = new System.Timers.Timer();
aTimer.Elapsed+=new ElapsedEventHandler(OnTimedEvent);
// Set the Interval to 600 seconds.
aTimer.Interval=600000;
aTimer.Enabled=true;
Console.WriteLine("Press \'q\' to quit the sample.");
while(Console.Read()!='q');
}
// Specify what you want to happen when the Elapsed event is raised.
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
Console.WriteLine("10 minutes passed!");
}
Timers aren't exact, just approximate. Don't use the logic of "just add 10 minutes". Each time your timer fires, you need to check for time skew and adjust.
eg. If you say "wake me up in 10min", and it wakes you up in 10min 1sec, then the next timer needs to be 9min 59sec, not 10min.
Also, you want to assign your next timer at the end of your logic.
eg. say you want to start taskA every 10min and it takes 2 seconds to run. Your timer starts and 10 minutes later it wakes up to run taskA. It kicks off, finishes, now you add 10 minutes. But it took 2 seconds to run your task. So 10 minutes from the time your code ran will be skewed by 2 seconds.
What you need to do is predict the next time you need to run and find the difference between now and then and set the timer to that difference.

Thread sleep/wait until a new day

I'm running a process in a loop which has a limit on the number of operations it does per day. When it reaches this limit I've currently got it checking the the time in a loop to see if it a new date.
Would the best option be to:
Keep checking the time every second for new date
Calculate the number of seconds until midnight and sleep that length of time
Something else?
Don't use Thread.Sleep for this type of thing. Use a Timer and calculate the duration you need to wait.
var now = DateTime.Now;
var tomorrow = now.AddDays(1);
var durationUntilMidnight = tomorrow.Date - now;
var t = new Timer(o=>{/* Do work*/}, null, TimeSpan.Zero, durationUntilMidnight);
Replace the /* Do Work */ delegate with the callback that will resume your work at the specified interval.
Edit: As mentioned in the comments, there are many things that can go wrong if you assume the "elapsed time" an application will wait for is going to match real-world time. For this reason, if timing is important to you, it is better to use smaller polling intervals to find out if the clock has reached the time you want your work to happen at.
Even better would be to use Windows Task Scheduler to run your task at the desired time. This will be much more reliable than trying to implement it yourself in code.
Windows has a task scheduler that handles exactly this duty. Create the program to do that which it is supposed to do. Then set it up as a scheduled task.
Just calculate a period to wait and run an asynchronous timer in this way you can avoid extra CPU consuming whilst waiting:
var nextDateStartDateTime = DateTime.Now.AddDays(1).Subtract(DateTime.Now.TimeOfDay);
double millisecondsToWait = (nextDateStartDateTime - DateTime.Now).TotalMilliseconds;
System.Threading.Timer timer = new Timer(
(o) => { Debug.WriteLine("New day comming on"); },
null,
(uint)millisecondsToWait
0);
Considering the two options you've provided:
There are 60*60*24 = 86,400 seconds per day, so you could potentially do a lot of checking if you hit the limit early. Additionally, busy waiting is a waste of CPU cycles, and it will slow down everything else that is running.
You should calculate the number of seconds until midnight and sleep that long (although I believe the sleep paramater takes ms rather than s, so a simple conversion may be needed).
EDIT:
An additional benefit of calculating then sleeping is that if a user wants to bypass your restriction by changing the clock, they will not be able to (since the clock reading midnight won't wake the process as it would with continual checking). However, with a better understanding of how your program works internally, the user could change the clock to almost midnight every time they are about to reach the limit of operations, causing the thread to wake up in a few minutes or even a few seconds. It's a more complicated exploitation than would be doable with your first suggestion, but it can be done.
This is how I make a thread sleep till tomorrow 6AM
minutesToSleep = (int)(new DateTime(DateTime.Now.AddDays(1).Year, DateTime.Now.AddDays(1).Month, DateTime.Now.AddDays(1).Day, 6, 0, 0) - DateTime.Now).TotalMinutes;
Console.WriteLine("Sleeping for {0} minutes (until tomorrow 6AM)", minutesToSleep);

Timer with interval of multiple months

I have a windows service handles events triggered by a System.Timers.Timer.
I want to set the interval of that timer to 3 months.
The Interval property of the System.Timers.Timer is an Int32 in millseconds, and Int32.MaxValue is smaller than 3 months in milliseconds.
What should I do?
You would re-think your design. Store the next time you want to execute your event (e.g. registry, file, database, ...) and then wake up periodically to check whether that time has passed. Even if you could set a System.Timers.Timer for 3 months, the system would likely reboot before the timer went off and you'd lose your event.
Another option would be to use a scheduled job executed by the Windows Scheduler. It would run a small program that sends your service a message saying that the event has occurred. This would be less resource intensive - though more complex - than waking up periodically to check whether the 3 months had elapsed.
I would use Quartz.net - the opensource enterprise job scheduler - http://quartznet.sourceforge.net/
You may use CRON like syntax- http://quartznet.sourceforge.net/tutorial/lesson_6.html
& you may store relevant job state to a DB easily (just incase the machine dies, which it will) - http://quartznet.sourceforge.net/tutorial/lesson_9.html, and lots more usefulness.
I've used Quartz.net on several production systems and to my knowledge the processes are still running today :)
Why not use System.Timer - http://quartznet.sourceforge.net/faq.html#whynottimer
Good luck!
You can't run your PC for continuously for 3 month. If application is closed timer will also close. On restarting application results the timer to restart from beginning.
If you want to fire event at interval of 3 months you must keep information about the total time elapsed by the timer at the time of exiting application.
Updated
So you have to divide your interval in some parts and set a counter on every elapse and increment it.Check it in every elapse until it reaches to 3 months.
e.g.
int interval = 10 * 24 * 60 * 60 * 1000; // Time interval for 10 days
int counter = 0;
private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
counter++;
if (counter == 9)
{
//do your task for 3 months
//since counter increments 9 times at interval of 10 days
//so 9*10=90 days i.e. nearly equal to 3 months
counter = 0;
}
}
Had the same problem, except that it was a generic system that could do the same thing with completely adjustable intervals, from milliseconds to months. Well, was supposed to. Turned out it indeed messed up on intervals larger than 24.8 days because of this.
In my case, "rethinking the approach" was of out of the question, since it was just one small problem sub-case of a much larger Windows service system.
The solution was rather simple, but do note I had a side system that supplied me with the next execution as DateTime; the timer's job was just to match that to actually execute the task, so it calculated the interval simply by subtracting DateTime.Now from that.
In this case, all I needed to do was keep an overflow boolean alongside my timer object, and setting that when checking that interval against Int32.MaxValue:
private Timer _timer;
private Boolean _overflow;
private void QueueNextTime(DateTime thisTime)
{
TimeSpan interval = this.GetNextRunTime(thisTime) - DateTime.Now;
Int64 intervalInt = (Int64)((interval.TotalMilliseconds <= 0) ? 1 : interval.TotalMilliseconds);
// If interval greater than Int32.MaxValue, set the boolean to skip the next run. The interval will be topped at Int32.MaxValue.
// The TimerElapsed function will call this function again anyway, so no need to store any information on how much is left.
// It'll just repeat until the overflow status is 'false'.
this._overflow = intervalInt > Int32.MaxValue;
this._timer.Interval = Math.Min(intervalInt, Int32.MaxValue);
this._timer.Start();
}
// The function linked to _timer.Elapsed
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
this._timer.Stop();
if (this._overflow)
{
QueueNextTime(e.SignalTime);
return;
}
// Execute tasks
// ...
// ...
// schedule next execution
QueueNextTime(e.SignalTime);
}
This is simplified, of course; the real system has try/catch and a system to abort on external stop commands. But that's the gist of it.
I would create an exe application and run as a part of scheduled Windows Task.

Categories