DispatchTimer skipping interval seconds displayed in the UI - c#

I'm having some trouble with my DispatchTimer. Every once in a while, it'll "skip" a second or two shown on my UI. It's started in my MainViewModel class where it displays a countdown in my view.
public string Timer
{
get { return _timer; }
set
{
_timer = value;
OnPropertyChanged("Timer");
}
}
public MainViewModel()
{
_countDownTimer = new DispatcherTimer();
_countDownTimer.Tick += async (o, e) => await countdownTimer();
_countDownTimer.Interval = new TimeSpan(0,0,0,1);
_countDownTimer.IsEnabled = true;
_countDownTimer.Start();
}
And here's the method that is called when the countdown goes to zero:
private async Task countdownTimer()
{
TimeSpan TimeRemaining = _next - DateTime.Now;
string t = TimeRemaining.Hours + "h " + TimeRemaining.Minutes + "m " + TimeRemaining.Seconds + "s";
this.Timer = t; // display it on the UI
int secondsRemaining = (int) TimeRemaining.TotalSeconds;
if (secondsRemaining == 0)
{
// do something that takes a while
await Task.Run(() => {
refresIp();
});
}
}
Side note:
The variable _next is simply 5 mins ahead. Once it goes to zero, it goes ahead another 5 mins from the current time: in other words, this countdown happens every 5 mins.
I'm not sure exactly what causes this "hickup", but maybe using await and Task is what's causing the issue versus using threads in this particular case. Though, from my understanding, is that you use await and async for something that can take a while waiting for the result, versus using threads to split up a particular task, hence why the use of await and async. Anyway, some insight would be great.

Related

How to call a method every x seconds for x time in Xamarin Forms?

I am making an application in Xamarin Forms in which I need to call a method every x time for x time (e.g. every 5 seconds for 2 minutes). How can it be done?
I have only found information on how to call a method every x time, but this is not enough for what I am looking for.
This is what I have tried. This calls MyMethod after 15 seconds have elapsed:
await Task.Delay(new TimeSpan(0, 0, 15)).ContinueWith(async o =>
{
MyMethod();
});
And this calls MyMethod every 5 seconds:
var startTimeSpan = TimeSpan.Zero;
var periodTimeSpan = TimeSpan.FromSeconds(5);
var timer = new System.Threading.Timer((e) =>
{
MyMethod();
}, null, startTimeSpan, periodTimeSpan);
What I need is to call MyMethod every x seconds for x amount of time.
Don't forget that Xamarin is based on C# language, so you can use C# language.
According to Microsoft documentation Timer class , you can do something like this:
public class Example
{
private static System.Timers.Timer aTimer;
public static void Main()
{
SetTimer();
}
private static void SetTimer()
{
// Create a timer with a five second interval.
aTimer = new System.Timers.Timer(5000);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += OnTimedEvent;
aTimer.AutoReset = true;
aTimer.Enabled = true;
}
private static void OnTimedEvent(Object source, ElapsedEventArgs e)
{
// Do your instruction while two minutes here
// You could create another Timer which repeat instruction during two minutes
}
}
You could set a limit seconds for the Timer.
For example you want do something every 5 seconds for 2 minutes.
int sec = 120000; // 2 minutes
int period = 5000; //every 5 seconds
TimerCallback timerDelegate = new TimerCallback(Tick);
Timer _dispatcherTimer = new System.Threading.Timer(timerDelegate, null, period, period);// if you want the method to execute immediately,you could set the third parameter to null
private void Tick(object state)
{
Device.BeginInvokeOnMainThread(() =>
{
sec -= period;
if (sec >= 0)
{
//do something
}
else
{
_dispatcherTimer.Dispose();
}
});
}
You could do something like this:
You'll probably need a Thread that is running in the background:
private async void CallMethodEveryXSecondsYTimes(int waitSeconds, int durationSeconds)
{
await Task.Run(() => {
var end = DateTime.Now.AddSeconds(durationSeconds);
while (end > DateTime.Now)
{
Dispatcher.BeginInvokeOnMainThread(() =>
{
YourMethod();
});
Thread.Sleep(waitSeconds*1000);
}
});
}

Trying to implement timer and perform tasks every x seconds but the tasks seem to get delayed

I'm writing an app where a user specifies a length of time, length of an interval and a length of time in between intervals. I want to have a timer label showing the user the total time but then I also want to have a label showing the work status (recording if in the interval, break if between interval time and break end).
Heres an Example: Total time = 2 min, Interval = 20 seconds, Break = 10 seconds
In this example there will be 4 intervals. So from 0:00-0:19 I want to display "Recording" and then from 0:20-0:29 I want to display break and then from 0:30-0:49 I display "Recording" and 0:50-0:59 I display "Break" and so on. All while the timer counts the time.
So I thought this would be pretty straightforward but what seems to happen is the timer increments properly but after the 1st interval the label doesnt switch from break to recording until 0:31 or 0:32 so it looks a little delayed.
Here is the code I am using currently (Note obs is an object Im passing in that has data from user input).
int TotalInterval = obs.Interval + obs.Break;
int WorkingInterval = obs.Interval;
int NumberOfIntervals = (obs.Duration*60) / TotalInterval;
DateTime ObservationEnd = obs.DateCreated.AddMinutes(obs.Duration);
Timer.Text = "Starting Timer";
int minutes = 0;
int seconds = 0;
int InIntervalCounter = 0;
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
// called every 1 second
Timer.Text = "Started";
if (ObservationEnd < DateTime.UtcNow)
{
Timer.Text = "Time Over";
Results.IsVisible = true;
return false;
}
else
{
seconds++;
InIntervalCounter++;
if (InIntervalCounter > WorkingInterval)
IntervalOrBreak.Text = "Break";
if (InIntervalCounter > TotalInterval)
{
IntervalOrBreak.Text = "Recording";
InIntervalCounter = 0;
}
Timer.Text = "Time: " + minutes + ":" + seconds.ToString("D2");
return true;
}
});
I'm pretty new to app development/xamarin so any help is greatly appreciated.
Try using simple Threads with Thead.sleep() like this:
final long delay_millis = 100;
Thread thread_something;
thread_something = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
try {
long start_time = SystemClock.elapsedRealtime();
// Do some task
long time_need_for_delay = (start_time + delay_millis) - SystemClock.elapsedRealtime();
if(time_need_for_delay > 0)
thread_something.sleep(time_need_for_delay);
} catch (Exception e) { }
}
}
});
thread_something.start();
after the 1st interval the label doesnt switch from break to recording
until 0:31 or 0:32 so it looks a little delayed.
If you want to display break from 0:20-0:29 and display "Recording" from 0:30-0:49, I think the if statement should change to InIntervalCounter >= WorkingInterval and InIntervalCounter >= TotalInterval, InIntervalCounter > WorkingInterval may cause the 1 second delay.

Create a class/method for a time (start, reset, stop, get istant, get timerun)

I'm building a racing game and I'm working on race times.
I try to build a system to start an instance of a timer with various options.
My little experience is putting me in crisis ... would some good soul want to help me?
This was the idea:
public class Timer {
public float counter;
public bool reset;
public string runtime = "--:--:--";
public string istant = "not istant";
public void startTimer()
{
/* inupdatealternative: counter += Time.deltaTime; */
if(reset == true)
{
counter = 0;
}
else
{
counter = Time.time;
}
var minutes = counter/60; // divide guitime by sixty (minutes)
var seconds = counter%60; // euclidean division (seconds)
var fraction = (counter * 100) % 100; // get fraction of seconds
runtime = string.Format ( "{0:00}:{1:00}:{2:000}", minutes, seconds, fraction);
Debug.Log("in Start: "+runtime);
}
public void resetTimer()
{
reset = true;
}
public string getTimerRuntime()
{
return runtime;
}
public string getTimerIstant()
{
istant = runtime;
return istant;
}
}
in update, for exemple:
var lapTimer = new Timer(); // create a new timer
if(Lap < Pilot.pilotlap )
{
lapTimer.startTimer();
Lap++
}
else if(Lap==Pilot.pilotlap)
{
timerLabel.text = lapTimer.getTimerIstant();
lapTimer.resetTimer();
lapTimer.startTimer();
}
in my head I'm sure someone has already dealt with it ... surely there will be something that manages the times and returns values ​​in various ways: does it exist? or is there anyway how to make or build such a thing?
There is, it's called Stopwatch, it's THE class used in C# to use precise timers, and it's located in the System.Diagnostics namespace.
Using your Update() example, you can use it like this:
// Create a new stopwatch instance
// If the timer is used repeatedly, just instantiate one at start and re-use the same,
// to avoid garbage generation
Stopwatch lapTimer = new Stopwatch();
if(Lap < Pilot.pilotlap )
{
lapTimer.Start();
Lap++
}
else if(Lap==Pilot.pilotlap)
{
lapTimer.Stop();
// ElapsedMilliseconds returns exactly what it says, so you may need to format the value
// before passing it to the timerLabel.text
timerLabel.text = lapTimer.ElapsedMilliseconds.ToString();
lapTimer.Reset();
lapTimer.Start();
}
You can read about the class (its methods, fields and properties) here:
Stopwatch Class Documentation
You are doing a lot of unnecessary bool and local fields copiing and setting there. I would simply use something like
public class Timer
{
private float _startTime;
public bool IsRunning;
// you don't need an extra reset method
// simply pass it as a parameter
public void Start(bool reset = false)
{
if(IsRunning && !reset)
{
Debug.LogWarning("Timer is already running! If you wanted to restart consider passing true as parameter.");
return;
}
_startTime = Time.time;
Debug.Log("in Start: " + GetFormattedTime(_startTime));
IsRunning = true;
}
// depending what stop should do
// since this doesn't use any resources while running you could also simply
// only stick to the Start method and pass in true .. does basically the same
public void Stop()
{
IsRunning = false;
}
// I didn't see any difference between you two methods so I would simply use
public string GetCurrentTime()
{
if(!IsRunning)
{
Debug.LogWarning("Trying to get a time from a Timer that isn't running!");
return "--:--:---";
}
var timeDifference = Time.time - _startTime;
return GetFormattedTime(timeDifference);
}
private static string GetFormattedTime(float time)
{
// e.g. time = 74.6753
var minutes = Mathf.FloorToInt(time / 60f); // e.g. 1 (rounded down)
var seconds = Mathf.FloorToInt(time - 60f * minutes); // e.g. 14 (rounded down)
var fraction = Mathf.RoundToInt((time - seconds) * 1000f); // e.g. 676 (rounded down or up)
// Use a string interpolation for better readability
return $"{minutes:00}:{seconds:00}:{fraction:000}";
}
}
then in your Update you don't want to use
var lapTimer = new Timer(); // create a new timer
all the time since it would create a new timer and you wouldn't get any tracked time ... you rather would use it only once like
private Timer timer;
// just in case you want to keep track of needed times per lap
public List<string> lapTimes = new List<string>();
private void Awake()
{
timer = new Timer();
lapTimes.Clear();
}
private void Update()
{
...
if(Lap < Pilot.pilotlap)
{
timer.Start();
Lap++
}
else if(Lap == Pilot.pilotlap)
{
var currentTime = timer.GetCurrentTime();
timerLabel.text = currentTime;
lapTimes.Add(currentTime);
timer.Start(true)
}
...
}
Note that I don't know if this is all you have in Update or how you use it but you probably also do not want to (re)start the timer and count up the Lap every frame your conditions are true ... there should be more checks involved to make sure this can only be called once per lap ...

More efficient way of implementing a countdown timer/clock?

I'm writing a small lap counter for slot car races as a little home project. I want to implement a countdown timer, which I've done with the following as a test:
private Thread countdownThread;
private delegate void UpdateTimer(string update);
UpdateTimer ut;
public LapCounterForm()
{
InitializeComponent();
//...
ut += updateTimer;
countdownThread = new Thread(new ThreadStart(startCountdown));
}
private void startCountdown()
{
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
System.Diagnostics.Stopwatch stopwatch = new Stopwatch();
long time = 0;
stopwatch.Start();
while (stopwatch.ElapsedMilliseconds <= 5000)
{
time = 5000 - stopwatch.ElapsedMilliseconds;
TimeSpan ts = TimeSpan.FromMilliseconds(time);
ut(ts.Minutes.ToString().PadLeft(2, '0') + ":" + ts.Seconds.ToString().PadLeft(2, '0') + ":" + ts.Milliseconds.ToString().PadLeft(3, '0'));
}
}
private void updateTimer(string text)
{
if (this.InvokeRequired)
{
this.Invoke(new Action<String>(ut), new object[] { text });
}
else
{
lblCountdownClock.Text = text;
}
}
When I start my thread, it works. I get my 5 second countdown like I want, but I can see that I'm using a lot of CPU in the process (12% of my 8 thread i7 2600k).
I figure I can reduce this load a lot by only updating the UI every 10 milliseconds instead of every millisecond, but I have no idea how to do such, other than using if(time % 10 == 0) before making the TimeSpan and updating the UI but I suspect that will be just as inefficient thanks to the while loop.
Am I reinventing the wheel? I'd like my timer to be as accurate as possible (at least for the slot car lap time recordings, perhaps the UI does not need to be updated so often).
EDIT: I tried commenting out the actual string manipulation and UI update as suggested in the comments. Now when I start my thread my entire UI hangs until the thread exits and I still get 12% CPU usage. I suspect that while loop is eating up a lot of CPU time.
Update: I went with the multimedia timer (here) posted by Kohanz as well as Daniel's answer. I no longer use another thread at all, I just make one of those timer objects and have a tick event handler calculating the time between clicking the start button and the tick event. I can even set the period for my ticks to 1ms so I get my cool looking countdown, and it's apparently using 0% CPU :) I'm quite happy with this.
Dont, just DONT go down this road. You are completely thinking of this in the wrong way. You are basically forcing your thread to freeze for no benefit.
Basically any game works this way: you have an update loop, and whenever that triggers you do neccessary stuff. So for instance if you want to know how much time, you ask some kind of "timer" how much has passed since something happened
Here's a much better way to handle this:
class MyStopwatch {
private DateTime _startTime;
private DateTime _stopTime;
public void start() {
_running = true;
_startTime = DateTime.Now;
}
public void stop() {
_stopTime = DateTime.Now;
_running = false;
}
public double getTimePassed() {
if(_running) {
return (DateTime.Now - _startTime).TotalMilliseconds;
} else {
return (_stopTime - _startTime).TotalMilliseconds;
}
}
}
A bit after the fact, but this shows a way in which you might achieve what you need:
public class LapTimer : IDisposable
{
private readonly System.Diagnostics.Stopwatch _stopWatch = new System.Diagnostics.Stopwatch();
private readonly ConcurrentDictionary<string, List<TimeSpan>> _carLapTimes = new ConcurrentDictionary<string, List<TimeSpan>>();
private readonly Action<TimeSpan> _countdownReportingDelegate;
private readonly TimeSpan _countdownReportingInterval;
private System.Threading.Timer _countDownTimer;
private TimeSpan _countdownTo = TimeSpan.FromSeconds(5);
public LapTimer(TimeSpan countdownReportingInterval, Action<TimeSpan> countdownReporter)
{
_countdownReportingInterval = countdownReportingInterval;
_countdownReportingDelegate = countdownReporter;
}
public void StartRace(TimeSpan countdownTo)
{
_carLapTimes.Clear();
_stopWatch.Restart();
_countdownTo = countdownTo;
_countDownTimer = new System.Threading.Timer(this.CountdownTimerCallback, null, _countdownReportingInterval, _countdownReportingInterval);
}
public void RaceComplete()
{
_stopWatch.Stop();
_countDownTimer.Dispose();
_countDownTimer = null;
}
public void CarCompletedLap(string carId)
{
var elapsed = _stopWatch.Elapsed;
_carLapTimes.AddOrUpdate(carId, new List<TimeSpan>(new[] { elapsed }), (k, list) => { list.Add(elapsed); return list; });
}
public IEnumerable<TimeSpan> GetLapTimesForCar(string carId)
{
List<TimeSpan> lapTimes = null;
if (_carLapTimes.TryGetValue(carId, out lapTimes))
{
yield return lapTimes[0];
for (int i = 1; i < lapTimes.Count; i++)
yield return lapTimes[i] - lapTimes[i - 1];
}
yield break;
}
private void CountdownTimerCallback(object state)
{
if (_countdownReportingDelegate != null)
_countdownReportingDelegate(_countdownTo - _stopWatch.Elapsed);
}
public void Dispose()
{
if (_countDownTimer != null)
{
_countDownTimer.Dispose();
_countDownTimer = null;
}
}
}
class Program
{
public static void Main(params string[] args)
{
using (var lapTimer = new LapTimer(TimeSpan.FromMilliseconds(100), remaining => Console.WriteLine(remaining)))
{
lapTimer.StartRace(TimeSpan.FromSeconds(5));
System.Threading.Thread.Sleep(2000);
lapTimer.RaceComplete();
}
Console.ReadLine();
}
}

Run and stop a method for a minute

timer1= new System.Windows.Forms.Timer();
timer1.Interval =60000; // 1 min
timer1.Start();
MyMethodName();
timer1.Stop();
MyMethodName()
-has a for loop for 90,000 entries (and some validations inside that for loop).
for (int i = 0; i <= 90000; i++)
{
//validations go here
}
When the time in timer1 is done for a minute, i want to stop executing other entries in the for loop.
For example, if 45,000 entries are done in a minute, i want to stop executing the method ie. stop the method after a minute.
However the above timer code, executes till all the 90000 records are done looping inside the for loop, somehow the method doesn't run for a minute? Any help?
Two things. Firstly Your timer code is not actually connected to the running of MyMethodName. A timer is designed to run processes when the time has elapsed (and possibly at regular intervals depending on how it is set up.
Secondly and more to the point of your question to abort a loop you have to put code inside the loop. The key would be to have a stopwatch or similar start before your loop and then at the beginning of your loop check how much time has elapsed. If it is a minute or more then break;.
The key thing to note is that you will not stop exactly on a minute but you will finish the iteration of the loop that is running when the minute expires and then stop. This is usually what you want since stopping processing midway through something may cause nasty side effects.
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i =0; i<=90000; i++)
{
if (stopwatch.Elapsed>TimeSpan.FromSeconds(5))
break;
Console.WriteLine(i);
Thread.Sleep(1000);
}
Note that Thread.Sleep is there just because otherwise I get through all 90000 iterations too quickly. ;-)
So you would likely need a much different implementation. Consider this:
public class MyForm
{
private BackgroundWorker _worker;
public MyForm()
{
_worker = new BackgroundWorker();
_worker.DoWork += (s, args) =>
{
var timer = Stopwatch().StartNew();
do
{
// do something
} while (timer.ElapsedMilliseconds < 60000)
};
}
}
and then when you want to run it:
_worker.RunWorkerAsync();
However, you could make it even more robust. You could pass the time in like this:
_worker.RunWorkerAsync(60000);
and then in the DoWork handler, do this:
while (timer.ElapsedMilliseconds < (int)args.Argument)
Further, with the BackgroundWorker, you could support cancellation. Just set the WorkerSupportsCancellation flag to true and then in the condition do this:
while (timer.ElapsedMilliseconds < (int)args.Argument && !_worker.CancellationPending)
so, if necessary, you could do this:
_worker.CancelAsync();
Hmm, use a stopwatch instead
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
for(int i= 0; i <= 90000; i++)
{
// Get the elapsed time as a TimeSpan value.
TimeSpan ts = stopWatch.Elapsed;
if(ts.Seconds >= 60)
break;
}
However the above timer code, executes till all the 90000 records are done looping inside the for loop, somehow the method doesn't run for a minute? Any help?
The timer will not raise events until you free up the UI thread, which won't occur until after the method completes fully.
If you want to prevent the method from running past a specific duration, you could handle it in your method directly:
MyMethodName(TimeSpan.FromMinutes(1));
Then, in your method:
void MyMethodName(TimeSpan maxRuntime)
{
DateTime expiration = DateTime.Now + maxRuntime;
for (int i = 0; i <= 90000; i++)
{
//validations go here
if (i % 100 == 0) // check every 100?
{
if (DateTime.Now > expiration)
break;
}
}
}
That being said, a better approach would be to push this into a background thread, and cancel as needed.
you can set a flag on in validations to see if it is done or not by hooking up an event handler to the Tick event in the Timer object
//in an area accessible to
//both elements
object readonly _lock = new object();
bool elapsed = false;
where your original code was
elapsed = false;
timer1= new System.Windows.Forms.Timer();
timer1.Interval =60000; // 1 min
timer1.Tick=((sender, everntArgs)=>
{
lock(_lock)
elapsed = true;
});
timer1.Start();
MyMethodName();
timer1.Stop();
Inside of MyMethodName
//inside the loop
for (int i = 0; i <= 90000; i++)
{
//validations go here
lock(_lock)
if(elapsed)
break;
}
If you use a CancellationTokenSource with the CancellationTokenSource(TimeSpan) constructor, it makes it easy to write a method that will cancel an action after a specified time.
You can write a method like so:
public static void RunTimedAction(Action<CancellationToken> action, TimeSpan timeout)
{
using (var cancellationTokenSource = new CancellationTokenSource(timeout))
action(cancellationTokenSource.Token);
}
And then you can write any action that takes a CancellationToken as a parameter, like this:
private void action(CancellationToken cancel)
{
int i;
for (i = 0; i < 1000000; ++i)
{
if (cancel.IsCancellationRequested)
break;
Thread.Sleep(10); // Simulate work.
}
Console.WriteLine("action() reached " + i);
}
Which you can use like this:
Console.WriteLine("Started at " + DateTime.Now);
RunTimedAction(action, TimeSpan.FromSeconds(10));
Console.WriteLine("Stopped at " + DateTime.Now);
Let's put this together into a complete demo program:
using System;
using System.Threading;
namespace Demo
{
class Program
{
void run()
{
Console.WriteLine("Started at " + DateTime.Now);
RunTimedAction(action, TimeSpan.FromSeconds(10));
Console.WriteLine("Stopped at " + DateTime.Now);
}
private void action(CancellationToken cancel)
{
int i;
for (i = 0; i < 1000000; ++i)
{
if (cancel.IsCancellationRequested)
break;
Thread.Sleep(10); // Simulate work.
}
Console.WriteLine("action() reached " + i);
}
public static void RunTimedAction(Action<CancellationToken> action, TimeSpan timeout)
{
using (var cancellationTokenSource = new CancellationTokenSource(timeout))
action(cancellationTokenSource.Token);
}
static void Main()
{
new Program().run();
}
}
}

Categories

Resources