Show System Time in WPF: Dispatcher timer - c#

I am displaying time in my application using(basically in a label Datetime.Now formatted as hh:mm:ss tt) using a DispatcherTimer.
_timer = new DispatcherTimer {Interval = new TimeSpan(0, 0, 1)};
_timer.Tick += _timer_Tick;
_timer.Start();
private void _timer_Tick(object sender, EventArgs e)
{
try
{
var now = DateTime.UtcNow;
var nowUserSelected =
TimeZoneInfo.ConvertTimeFromUtc(now,
(DataContext as MainViewModel)?.UserSelectedTimeZone ?? TimeZoneInfo.Local);
ClockCurrent.Text = $"{nowUserSelected:hh:mm:ss tt}";
}
catch (Exception exp)
{
Log.Error("Error occurred while reporting time", exp);
}
}
The issue is that if the user opens a the Clock from the task bar(if you click on the time in the task bar there is pop up which displays the current time including seconds) sometimes the time in my app appears to be off by one second(sometimes!). Which I believe could be happening because the timer must be set to tick every second from the current time which could be 10:50:53 PM and 950 ms and it might be lets say and then it ticks every 54 and 950 ms ..... ms and so on , and since I am only displaying time till seconds it gives the illusion i am behind a whole second.
What is the recommended way to show the time which matches the time shown by the task bar, maybe set timer interval to tick every 500 ms ?

Check the ms when you are starting. Do first refresh right after full second, next refreshes each second
Even better: at the end of UI update you determine when the next UI update must happen.

#iPirat is correct, but I guess others did not understand what he meant, so let me first explain what is the problem and then how to solve it.
The DispatcherTimer runs on the WPF GUI thread. The advantage of this is that the code of DispatcherTimer.Tick can access any WPF control. The disadvantage is that once the 1 second since the last tick has expired, the WPF GUI thread might be busy with something else and the tick fires few milliseconds later. The next time Tick gets execute, there will be another small delay and this accumulates until the summed up delay becomes bigger than 1 second and you "loose" one tick.
You can prevent that by changing DispatcherTimer.Interval during each tick. If you want a tick every 1000 milliseconds and the tick was executed x milliseconds too late, you set DispatcherTimer.Interval to 1000-x milliseconds.
const int constantInterval = 1000;//milliseconds
private void Timer_Tick(object? sender, EventArgs e) {
var now = DateTime.Now;
var nowMilliseconds = (int)now.TimeOfDay.TotalMilliseconds;
var timerInterval = constantInterval -
nowMilliseconds%constantInterval + 5;//5: sometimes the tick comes few millisecs early
timer.Interval = TimeSpan.FromMilliseconds(timerInterval);
}
By the way, you might want to increase the priority like this DispatcherTimer timer = new DispatcherTimer (DispatcherPriority.Input);. This will make the delays a bit smaller, but the GUI gets still rendered fast enugh.
For more details see my article on CodeProject: Improving the WPF DispatcherTimer Precision

Related

C# Timer -- measuring time slower

I'm writing a code where:
I.)
The user adds "events" during run-time. (To a flowlayoutpanel) These events are turning some LEDs on/off, after "x" time has elapsed and the LED-turning functions are written in a Led-function.cs class.
i.e:
1) Turn left led on After 3500ms
2) Turn right led on After 4000ms
II.)
When the user hits start a timer starts.
// Create timer.
System.Timers.Timer _timer;
_timer = new System.Timers.Timer();
_timer.Interval = (1);
_timer.Elapsed += (sender, e) => { HandleTimerElapsed(LedObject, device, _timer); };
_timer.Start();
III.)
The timer's tick event is raised every millisecond and checks if the user definied time has ellapsed. Im measuring the elapsed time with adding +1 to an integer at every tick event. (NumberOfTicks++;)
//Timer Handle
private void HandleTimerElapsed(Led_Functions LedObject, string device, System.Timers.Timer _timer)
{
NumberOfTicks++;
if (NumberOfTicks >= Start_time[0])
{
LedObject.LeftLED_ONnobutton(device);
}
}
IV.) What I noticed was that when the tick was set to 1. (So the tick event is raised every millisecond) Even if I set 3000ms to the evet the LED actually flashed around 6 seconds.
When the tick was set to 100. (So every 0,1s) then the flash was more accurate (3,5sec or so).
Any Ideas why im having this delay in time?
Or do you have any ideas how could I implement it better?
Thank you!
1ms is a really small interval for CPUs. You cannot ensure your code get called every 1ms especially when the system has some heavy work to do.
A better implement is to use DateTime.Now - startTime to get the time span or use Stopwatch.
private Stopwatch stopwatch = new Stopwatch();
// start the stopwatch:
stopwatch.Start();
// Timer Handle:
numberOfTicks = stopwatch.ElapsedMilliseconds;

Possible causes for thread/timer delay?

In relation to a previous question of mine ([question] : Which thread will timer method run in? ), I've added a Timer to my Windows Forms app to run every 100ms to show how long the program session has been running. I've defined it as follows (these are only the snippets relevant to the timer):
private System.Timers.Timer timerPureTime = new System.Timers.Timer(100);
timerPureTime.Elapsed += new System.Timers.ElapsedEventHandler(updateTimeElapsed);
this.timerPureTime.SynchronizingObject = currentForm; //where currentForm is my main Form
public void updateTimeElapsed(object sender, ElapsedEventArgs e)
{
if (currentForm.lblTimeElapsed.InvokeRequired) //lblTimeElapsed is your standard Windows Form label
{
currentForm.lblTimeElapsed.Invoke((MethodInvoker)delegate //also, trying to make make GUI invoking thread-safe here
{
TimeSpan t = TimeSpan.FromSeconds(purelyTime);
string showTime = string.Format("{0:D2} min {1:D2} sec",
t.Minutes,
t.Seconds);
currentForm.lblTimeElapsed.Text = showTime;
});
}
else
{
TimeSpan t = TimeSpan.FromSeconds(purelyTime);
string showTime = string.Format("{0:D2} min {1:D2} sec",
t.Minutes,
t.Seconds);
currentForm.lblTimeElapsed.Text = showTime;
}
purelyTime += 0.1;
}
As I understand it the Timer should be running in a thread of its own (taken from the Threadpool) however it still experiences some delay every now and then, throwing the timer off-course. Other threads within the application run pretty regularly (every 250ms) and computation-intensive, but shouldn't these be independent of Timer threads?
What could be the possible causes for timer lag in such cases?
Windows cannot guarantee a precisely regular callback for a timer, so you will definitely see that kind of variance.
You need to take a different approach:
Initialise a Stopwatch field in your class.
Call Stopwatch.Restart() when you want to reset the timing.
Inside updateTimeElapsed() use Stopwatch.Elapsed instead of purelyTime.
Note that your code is completely ignoring the amount of time spent in the timer handler function itself. Inside the handler, you are using Invoke to send a message to the UI and waiting for it to return. That can take an arbitrary amount of time, particularly if the UI is busy.

How to execute code at a specific exact time (far from now) in C#?

I am developping a Windows Service that has to execute a method at a specific exact time.
When the service starts, it extracts several different DateTimes from an SQL Server database and should then start a thread for each DateTime. Each thread should wait until the exact DateTime value, then do some work, then wait again for a shorter period of time and then do some final work again.
I don't want to use System.Timers.Timer because:
the method has to be executed at the exact specified time
this exact time could be in more than 24h
I need to pass a parameter to the Elapsed event
How can I implement this?
Thank you.
EDIT: I just had an idea last night, what if I write a dynamic polling timer? For exemple, if the due datetime is in more than 12 hours it can poll the time every hour to re-synchronize. Then if the due datetime is in more than 3 hours it can poll the time every 30min and so on... What do you think? Would that be a bad code to write?
Thank you again.
You can't pass a parameter to the tick event with System.Timers.Timer, but you can with System.Threading.Timer. The delay time "greater than 24 hours" is no problem. The timers use a 32 bit period that is expressed in milliseconds. 2^31 milliseconds works out to something like 24.85 days. There's also an overload that lets you specify the delay with a 64-bit signed integer. 2^63 milliseconds is ... several hundred million years.
But having those timers tick at an exact date/time far in the future is problematic because you have to specify the delay time, and a clock change will cause your delay to be too short or too long (think Daylight Saving Time, or user-initiated time changes). You can specify the tick time and have a timer poll the current time every second. It's kind of ugly, but it works.
Another option is to use the Windows Waitable Timer object, which lets you set an exact date and time. Unfortunately, the Framework doesn't include a corresponding object. I wrote an article about it some years ago and published the code. The article is no longer available online, but you can download the code from http://mischel.com/pubs/waitabletimer.zip.
I have used something called ManualResetEvent in the past with a lot of success. Assuming that you know the exact time to run your code then you will be able to calculate estimated time to run (TTR) and subsequent runs should be configured within the first run.
partial class SomeService : ServiceBase
{
private ManualResetEvent stop = new ManualResetEvent(false);
private List<DateTime> times;
private int idxDT = 0;
public SomeService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
this.stop.Reset();
//implement you logic to calculate miliseconds to desired first run time
int miliseconds_to_run = 1;
ThreadPool.RegisterWaitForSingleObject(this.stop,
new WaitOrTimerCallback(ThreadFunc),
null,
miliseconds_to_run,
true);
}
private void ThreadFunc(object _state, bool _timedOut)
{
if (_timedOut)
{
if(this.times == null)
{
//get a list of times to run, store it along with the index of current TTR
this.times = new List<DateTime>();
}
int miliseconds_to_run = (this.times[this.idxDT++] - DateTime.Now).Miliseconds;
ThreadPool.RegisterWaitForSingleObject(this.stop,
new WaitOrTimerCallback(ThreadFunc),
null,
miliseconds_to_run,
true);
}
}
protected override void OnStop()
{
this.stop.Set();
}
}
Of course this highly depends how exact your job start time has to be. ThreadPool class will send a Thread alocation request to the OS, and then it will wait for the next available Thread from the Pool. In some processes with lots of lots of threads this could lead to Thread starvation and your exact times will be late.
You could also try creating Task Scheduler Jobs from .NET but I've never done that before.
Even though you said you don't want to use System.Timers.Timer I'll show you how to do it anyway, as bullet 1 and two in your question don't seem to be valid points against using it in my opinion.
So here's what I do in several applications of mine:
// 1. Create the timer
System.Timers.Timer timer = new System.Timers.Timer();
timer.AutoReset = false;
timer.Elapsed += ...;
// 2. Calculate the number of milliseconds between the scheduled time and the current time
timer.Interval = (scheduledDate - DateTime.Now).TotalMilliSeconds;
// 3. Start the timer
timer.Start();
Done. The event will fire "roughly" at the desired time. Please note that this doesn't work down to the exact millisecond, as Windows is not a real-time OS.
I did face sort-off similar issue and used below solution which might solve your issue.
I used ManualResetEvent class, added a Wait method and made the service wait for the exact time.
protected ManualResetEvent waitEvent = new ManualResetEvent(false);
protected bool Wait(int milliSecs)
{
return !this.waitEvent.WaitOne(milliSecs, true);
}
I used this Wait in OnStart method as below:
protected override void OnStart(string[] args)
{
DateTime nextRun = dt;//datetime from the database
YourProcessOrThreadToRun process = new YourProcessOrThreadToRun();
while (Wait((int)nextRun.Subtract(DateTime.Now).TotalMilliseconds))
{
process.StartProcess();
}
}
YourProcessOrThreadToRun is the thread you want to run on that exact time.

how to set the timer interval for one day in a windows service

I have created a windows service.In which I have set the timer interval for every one minute and so it was triggering for every minute,. But I need to trigger the same for each day..
You can find the code below where I have set the timer interval in OnStart() method..
Code:
protected override void OnStart(string[] args)
{
TraceService("start service");
//handle Elapsed event
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
//This statement is used to set interval to 1 minute (= 60,000 milliseconds)
timer.Interval = 86400000;
//enabling the timer
timer.Enabled = true;
}
This post is quite old but I think it's worth mentioning that instead of using a magic number like 86400000 it's better to use something like new TimeSpan(1, 0, 0, 0).TotalMilliseconds , so that if someone needs to change it they will know what should be changed.
So, couple of things regarding your post:
First, it does not state what is the problem!
Technically, Timer supports a timer interval of about 25 days.
So, your code should work.
If you want to exceed 25 days and you are not concerned about thread safety, I suggest you go to System.Threading.Timer.
More info here
Since, your service runs as a windows based service, i suggest you go to System.Threading.Timer.
In C# now you can use TimeSpan class to get total milliseconds in day to set a per day timer.
var totalMilliSecondsPerDay = TimeSpan.FromDays(1).TotalMilliseconds;
var timer = new Timer(totalMilliSecondsPerDay);

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

I am trying to get a timer run every minute in sync with the system clock (00:01:00, 00:02:00, 00:03:00, etc). Will the following code produce a stable timer? I've tried running it. The timer does precisely run every min. But the question is will it start to wander off, say, after several months?
private System.Timers.Timer timer;
public frmMain()
{
timer = new System.Timers.Timer();
timer.AutoReset = false;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Interval = GetInterval();
timer.Start();
}
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
//Do processing
timer.Interval = GetInterval();
timer.Start();
}
private double GetInterval()
{
DateTime now = DateTime.Now;
return ((60 - now.Second) * 1000 - now.Millisecond);
}
It won't.
DateTime.Now uses the system clock as the reference timer, so your timer will always stay in sync with it, modulus the few milliseconds of system clock resolution.
I don't see how it can wander off, everytime the timer ticks, it determines how long it is to the next exact minute and sets the interval accordingly. In theory it is actually quite ingenious, because I timer by itself would wander off. The only way to know for sure would be to test it of course, so as long as your UI doesn't freeze for more then 60 seconds, (maybe 50s to play it safe) there won't be a problem. Keep in mind that laptops will often go to sleep when left for a time, or the lid is closed, this will cause a glitch.
Depending on what you are attemping to do though, why not just use the system time, to send an event everytime the minute changes?
The big picture looks fine, GetInterval will always return a positive value, and so on. I only see a few caveats:
If standby/resume happens then you may have one run at a non-round time
If timer_Elapsed takes more than a minute to complete then you will miss a beat
If the system is so overloaded that timer_Elapsed is scheduled 40/50 seconds in delay AND timer_Elapsed takes more than 60 - delay seconds, you will miss a beat
Your code won't drift, but there will (inevitably) be jitter as Timer callbacks are rarely bang on the requested interval.
The only potential problem I can see with this code is if the ThreadPool were under such strain that your timer callback was not dequeued and executed within a minute interval, resulting in a skipped minute.
This seems somewhat unlikely, but not completely beyond the realms of possibility.

Categories

Resources