Fire method after variable amount of time - c#

I am building an alerter service (windows service) in c#. This service will start and do a few api calls. based on the result I get from the calls and the setting that apply to that user an other method will fire after x amount of time. This goes on until the user stops the service.
The time in between the calls to the methods can be variable. So after startup the first method call can be after 1 min, after that the next call can be after 5 min, the call after that can be after 10 min. All depends on the response I get from the API.
I have been looking into system.timers, but every example i find has a fixed time the event fires so that does not fit my needs.

Define the time variable globally and after each response reset the interval time
FYI
1 Minute = 60000 so for 10 Minutes aTimer.Interval=60000 * 10;
use it like this
//defined globally
System.Timers.Timer aTimer = new System.Timers.Timer();
aTimer.Elapsed+=new ElapsedEventHandler(OnTimedEvent);
//api call goes here
aTimer.Interval=5000; // here you can define the time or reset it after each api call
aTimer.Enabled=true;

Use a one-shot timer, and reset it after every call. I typically use System.Threading.Timer for this:
// When you first create the timer, set it for the initial time
Timer MyTimer = new Timer(TimerCallbackFunction, null, 60000, Timeout.Infinite);
Setting the period to Timeout.Infinite prevents the timer from ticking multiple times. It'll just tick once and then wait indefinitely, or until you restart it.
In your timer callback, do whatever needs to be done and then reset the timer:
void TimerCallbackFunction(object state)
{
// Here, do whatever you need to do.
// Then set the timer for the next interval.
MyTimer.Change(newTimeoutValue, Timeout.Infinite);
}
This prevents multiple concurrent callbacks (if your timeout value is too short), and also lets you specify when the next tick will be.

Related

C# .NET6.0 System.Timers: When elapsed event is fired the eventhandler is executed multiple times

I'm rather new to .Net development.
I have the following scenario:
In my company we have machines that register cycle times (how long does the machine take to make 1 product). I have created a console application in .Net 6.0 that captures these cycles that are sent from the machines (via OPC server). There are expected cycle times and when the current cycle is exceeding these expected times then my console app will register that. Here's where the issue occurs.
To know that the current cycle exceeds its expected times I start a System.Timer when a cycle ended. If the cycle exceeds its expected times by 25% the timer's elapsed event is fired. The smallest interval I have in my timers is 150 seconds. Each machine has its own timer variable.
When the elapsed event is fired I want the timer in question to STOP and not fire that event again in 150 seconds. However, the way it is setup now, the eventhandler is executed about 50+ times in 1ms. I used DateTime.Now in a field of the record that is registered by the eventhandler and they are all on the exact same time. Since my interval is 150 seconds I know my code isn't executed slower than the interval hit.
I extended the System.Timer so I could pass an object with the timer.
public class MachineTresholdTimer : System.Timers.Timer
{
public Machine currentMachine { get; set; }
}
This is the method I use to start a timer from a machine when a cycle is registered:
void StartTimerTreshold(MachineTresholdTimer machineTimer, decimal timePerPanel)
{
if (timePerPanel != 0)
{
if (machineTimer.Enabled) machineTimer.Stop();
machineTimer.Interval = (double)((timePerPanel * 1000) + 50000);
machineTimer.Elapsed += OnExceededTreshold;
machineTimer.AutoReset = false;
machineTimer.Start();
}
}
If the timer is still running from the previous cycle I stop it and restart it since this means the previous cycle ran between the expected cycle times.
Since the Timer's property AutoReset = false I expected the Timer to stop when the event is triggered, as per documentation.
The method that triggers the function to start the timers is defined as async. The timer's event handler is not async. Since I am still wrapping my head around asynchronous tasks I don't know if this could be a problem.
With all this said now, what occurs is the following:
My eventhandler is executed about 50+ times. I create a record by accessing my own API endpoint to register an error via a dataservice. All the functions of my dataservice are async tasks. Even though the timer's interval is 150+ seconds I get 50+ records registered in my database from 1 (as I would expect) elapsed event. I use DateTime.Now in my Console application to fill a field for the record and this is the results I get:
All on the exact same time. What am I missing here? Are Timers even the right tool to use in this scenario? Any tips or nudges in the right directions are most welcome.

timer.interval increasing interval between calls

So, I have a basic timer set to wake a windows service every minute
Timer timer = new Timer();
timer.Interval = 1000 * 60; // 60 seconds
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
timer.Start();
It works most of the time, but I noticed about every hour it will increase the interval of elapsed time in 1 second:
[...]
Service is recall at 9/25/2019 1:26:52 AM
Service is recall at 9/25/2019 1:27:52 AM
Service is recall at 9/25/2019 1:28:52 AM
Service is recall at 9/25/2019 1:29:52 AM
Service is recall at 9/25/2019 1:30:53 AM
Service is recall at 9/25/2019 1:31:53 AM
Service is recall at 9/25/2019 1:32:53 AM
[...]
This caused the service to skip an service recall when minute changed:
Service is recall at 9/20/2019 2:38:59 AM
Service is recall at 9/20/2019 2:40:00 AM
Service was not recall at 2:39.
I think it happens because timer will wake up after elapsed time, not necessarily at the exact time the interval is met, and that is ok (I don't need huge precision, as long as the service is awaken every minute). I just don't want the interval between calls to be incremented by 1 second every hour or so.
I tried to calculate a new interval (instead of 60000 milliseconds) to correct elapsed time, but it is not uniform.
Any ideas on how to prevent this from happening?
The timer fires every minute as you expect but the timer does not take in to account the amount of time it takes to process the information in the function. If you need it to fire every minute, you will need to calculate the amount of time it took to process your information and then change the amount of time before the timer fires:
private void OnElapsedTime()
{
Stopwatch watch = new Stopwatch();
watch.Start();
//DO YOUR WORK HERE
Timer.Change(Math.Max(0, 60000 - watch.ElapsedMilliseconds));
watch.Stop();
}
This is not 100% precise but should give you the desired results.

C# Best way to schedule recurring task in an efficient windows service

I write a Windows Service in .Net Framework 4.0 and I need to schedule a recurring task inside. New task can only run if previous one is finished, so no tasks in parallel...
All my task occurences work on same object (WCF Channel factory).
A task takes almost 2 seconds to complete and may be scheduled every 2 seconds or every hour.
My constraint is to have this Windows service as invisible/light as possible on memory and processor uses point of view...
I've already found these 2 ways:
Use a System.Timers.Timer with Autoreset to false => I've to
implement an ElapsedEventHandler and pass my shared object (WCF
Channel factory)
Use a never ending loop: not sure of mem/proc use in
that state but no threads aspect to take care of.
Any suggestions?
Thanks and have a nice day!
For me was fine following: I'm started timer once, then in Tick method I will schedule next Tick call. Like this:
private Timer _timer;
//Interval in milliseconds
int _interval = 1000;
public void SetTimer()
{
// this is System.Threading.Timer, of course
_timer = new Timer(Tick, null, _interval, Timeout.Infinite);
}
private void Tick(object state)
{
try
{
// Put your code in here
}
finally
{
_timer?.Change(_interval, Timeout.Infinite);
}
}
// dont forget to dispose your timer using await _timer.DisposeAsync(); or _timer.Dispose();
System.Timers.Timer is the way to go and will have little to no system performance impact. Current systems can handle thousands of timers.
Since you want the timer to continue, don't set AutoReset, but you will need a way to change its interval (if that's what you need).
Your service instance should hold an instance of your WCF channel factory.
To ensure synchronous processing, you should implement an Interlocked protected flag, like a long, which can serve as an indicator of busyness. If equal to one, for example, the method that kicks off processing from timer elapsed event will simply return. Once processing is complete, at which time you set the flag to zero, further timer elapsed events will be able to enter and kick off processing again.
Remember to stop, restart, and dispose of your timer in the various service events, like pause, stop, start.

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;

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);

Categories

Resources