@@-Chris_ What I see, API change. You get now possibility to write async code instead callbacks. It also rescue you from callback overlapping (if you don't consider it).
This is useful in situation when your background task is lightweight/you can be sure your tasks finish in time. If you cannot be sure, the very first solution is better as it makes sure it does not overload your system. So, it really depends on what you want to solve, and at what cost (like instead of the first solution, does it worth using throttling?, etc.).
Hey Nick, you are reading my mind! I was thinking through such a thing just this weekend. You are doing great work for us all. Thank you and keep on streaming )))
@@mbalaganskiy I will try it out, I have an implementation using Cron expressions based on the IHostedService interface, which contains a System.Timers.Timer. I intend to test the PeriodicTimer implementation with a new version of my package.
_"PeriodicTimer is effectively a semaphore, allowing only one consumer to work"_ -- that's not how I read the documentation. **"This timer is intended to be used only by a single consumer at a time: only one call to WaitForNextTickAsync(CancellationToken) may be in flight at any given moment."** That sounds like the opposite of a semaphore to me. I.e. a semaphore is specifically designed to support two or more consumers blocking on the semaphore at a time, while PeriodicTimer is explicitly documented as supporting only one at a time.
This is what I do to get around the initial problem: var delayTask = new Task.Delay(5000); await DoWork(); await delayTask; This gets around the missing milliseconds, as the delay starts before the work commences. Works fairly well... not sure if there's an eventual creep, but if you need a pretty good approx of every X seconds, real simple.
Note: at 5:26, the `!stoppingToken.IsCancellationRequest` condition should come before the call to `WaitForNextTickAsync`, as `WaitForNextTickAsync` throws a TaskCancelled exception when the CancellationSource cancels.
The DispatcherTimer is for use with WPF applications, it schedules work on the Dispatcher thread of your application at a specific priority (WPF Dispatcher Priority, not thread priority). There are multiple priority levels, such as Background, Input, DataBind, Input, Normal, Render, etc. So for example, you could have an event that fires on the Dispatcher thread every 10 seconds at a DispatcherPriority.Background priority level, so that anything else running on the Dispatcher thread currently with a greater priority will take precedence. It's fairly specialized, so while it's possible to replace the functionality using the PeriodicTimer using functions like Dispatcher.Yield, I don't think you'd need/want to in most cases where people are using a DispatcherTimer.
Dont forget that as you cancelled/disposed the CancellationTokenSource you will need to new one up in the Start() method if you are thinking of re-starting the same BackgroundTask instance
I can actually use this. One service I wrote sends reminders to users and it checks whether or not it needs to on a daily basis. Used to be you'd write a windows service for such a thing and you might even do so today, but I wasn't prepared to go through that so I set up a scheduled task in Windows to open a web page every day. As you can guess, this is not ideal but now using this timer, I need only have 1 page open as long as I keep it open anyway. Maybe I can update that scheduled task to check if the window is open lol
In your console app, I believe if an Exception was thrown in the DoWorkAsync method, it would not get thrown until after the task was awaited, which would be after user hit enter and the await task.StopAsync ran
2 года назад+1
This is nice to know, and quite handy in some situations.
I've always done this by mod of time and compare against a previous which I'm guessing is what the tick is doing under the hood but nice to have a more idiomatic way of doing this...
Exactly. You don't just delay 1000ms. You can store start time, then delay (1000 - ElapsedMiliseconds). There are also other (more complex) ways to do this.
PeriodicTimer is great. I remember this workaround in the older days: protected override async Task ExecuteAsync(CancellationToken stoppingToken) { var sw = new Stopwatch(); while(!stoppingToken.IsCancellationRequested) { sw.Start(); // Some long running DoWorkAsync() task (~250ms) await Task.Delay(250, stoppingToken); // Wait for the rest of the second to elapse await Task.Delay(1000 - sw.Elapsed.Milliseconds, stoppingToken); sw.Reset(); } }
Immediately got rid of my Timer.Timer timers to do background work and replaced with this. Not only is it more accurate, it simplified things being async.
I have used the "Wrong Approach" many times. It's not really wrong; I don't want it to loop at fixed intervals, I just want it to pause briefly between units of work, and for that it's fine. Timer drift is an issue though, and there are again naive but good enough ways to deal with it: If your waits are long, just adjust future sleep and do a catch-up (e.g. if you loop at 60s and the task took 5462ms, then sleep for 54538ms, and factor drift with a stopwatch). If they're short, use a stopwatch to track how far behind schedule you are, and skip some when you're too far behind. This Periodic Timer will skip ticks if the tasks takes too long; if Nick had paused for 1100ms, the task would've executed every 2s, so just be aware of that quirk if you use this.
Nice topic! I use timer in net framework apps and always seemed to me that they are a pain in the ass to deal with. A timer that is awaitable looks like in in synch with the new task sintaxis! I'll be taking a closer look to this for sure.
What would be the correct way to have multiple timers in background service? Let's say I want two timers (one with 3s period and second with 5s). Should I fire inside ExecuteAsync two tasks where each handles its own timer and wait for both tasks to finish (when CTS is cancelled). Or going back to old timers with callbacks?
My main use case for a timer-like thing is a scheduler where I need the "DoWork" method executed at a precise time, i.e. every day at 11 a.m. So for that requirement I have a simple helper method called DelayUntil which calls "await Task.Delay" every 1000 ms until the expected time reached. I am not sure if it is the best practice for such a situation and if I can use this periodic timer to implement a better solution for my requirement but hey, it is simple and it works.
For precise timing using the ExecuteAsync, the user should use Task.Delay using the time difference between the next DateTime and the current time (and therefore "guarantee" that timing is aligned e.g. per second).
Be aware that this is not necessarily precise and actually holds a potential surprise... We were actually using a method like that, it seemed to work nicely. However when we ported it to Linux, the method sometimes was invoked several times every interval. Reason was that unlike what we saw ond windows, where the Task would awake with a slight delay, on linux it was awoken a bit early...
I prefer the first method. Its a lot more important to keep constant delay between operations than to execute them at exactly the same time of a second (or period)
@Nick thank for this awesome video on Timer. I have a question on point that you mentioned on 8:11 timeframe that when we have await in the loop then code does not continue. i wanted to understand problem of having await in the loop. Is it possible to explain it with an example. it will be help. Thanks.
Nice video :) what happens if an execption is thrown within the DoWorkAsync method? It won't be passed up, right? So it will repeat forever? And I think you forgot to _timerTask = null in the StopAsync method, this is only needed for sure if you want to reuse this. Therefore you would also need a null check in the StartMethod
I've never seen this method of starting a new task, that is, by simply assigning the method call to a Task variable. Has this always been possible? Is it a shorthand for Task.StartNew() or have I missed out/misunderstood all this time? Is this considered best practice in async programming for immediately executing a task on the thread pool from a method returning void?
@@nickchapsas Thanks for clarifying! Strange I have never encountered this before, but (yet again) I learned something new watching your videos - thanks man! I can definitely see several use cases for this.
If I have a web app with a BackgroundService and PeriodicTimer and I want to change the timer interval, how do I do it without restarting the web app? Even if you inject IOptionsMonitor you have already initialised the service and timer - any ideas?
I guess... Perhaps use his sample class as a template and determine when it gets fired up. Being that MAUI supports the Hosting model that now permeates all .NET Core apps, you will get a lot of additional richness with it.
Is it possible to register multiple scheduled services using PeriodicTimer and run concurrently? Based on the documentation, it does not seem it is possible.
I'm curious as to what happens if the timer interval is shorter than the work operation. Do you get tightly packed back to back calls? Does it drop the calls until the next window elapses? Is there any way to guarantee a specific frequency or call rate?
Would this be appropriate for a heatbeat monitor app? We will potentially have hundreds (or even thousands) of IoT devices deployed that will be sending back heartbeat pings every X seconds and we need to raise an alert if we don't hear the heartbeat in the specified interval (if another heartbeat is heard we cancel the previous countdown to raising an alarm and set a new one). Would this type of timer be good for this scenario or is there a better way to do this?
i have a resource httpClient and i wait until other sync/download processes donot use this httpClient and then the caller can use this httpClient to post a new message. because of a loop inside that waits for the httpClient to be available i cannot use await. How should i code this, a sharing httpClient and somekind of semaphore to indicated that it is in use and the caller that wants to use httpClient should wait.
I like using reactive timers/delays and subscribe to them. No issues with tasks that take longer (since the timer is decoupled from the subscription) and very clean code. What are your thoughts?
Yup, I'm doing the same when using timers, but reactive is also meant to fix this old school events pattern, so I guess it's no surprise both solution work nicely.
I can't help but think the video miss a mention of IHostedService and the fact that you can add hosted services to a console app by using the generic host builder. Sure, this solution works without the host builder, but if you want DI in a console app, why not use a host builder as well? I might be missing the point though 🙂
The point is that people should know how to use a feature without depending on a completely separate one. Knowing how to do those things independendly expands your knowledge scope
In fact, sometimes you need a timer which doesn't count time duration took by the work, like the very first example in this video. Imagine what if the work takes more time than your timer duration. Your work queue will grow infinitely. Instead of that you want to wait 1 second - do the work whatever time it takes - wait 1 second - do the work - do the work whatever time it takes - and so on. Is there some new class for that?
I find that usually I don't want something to happen periodically, as much as I want it to happen on a schedule... to that end, this kind of functionality is *very* rarely used for anything complicated. Maybe for some semi-low level things like reading from a memory channel and processing it in the background at certain intervals.
That's where I would store some configurable schedules somewhere and have the timer check to see what needs to be executed and spin it off in its own thread.
What with cases when timer should be created dynamically in various number ? For example, one timer created per one request. Every action should by async, so input all timers to while condition (and use only one at the time) is not expected. Why is Event Handling bad ?
silly question: does anyone know how does he set the output to print to the IDE's output window and not to the console, is it possible to do in VS2022 or only in that JetBrains IDE ?
one way is to use Debug.WriteLine instead of Console.WriteLine in a VS2022 console application. This will print the message to the IDE's output window.
I used NCrontab with a combination of.... what.... uh.. Nick just showed **not** to do . I'm now looking at implementing his approach. I had no idea a new timer was implemented.
@@nickchapsas Thanks for the answer. That's what I'm already doing, but I was curious if there was any other way, since it's, in the end, keeping the app running, which is really not a good thing for an API. The solution was to have a separate API to do theses scheduled tasks.
I belive you cannot Start and StopAsync task several times in your Console App setup as you call Dispose on your CancellationTokenSource in StopAsync. So you need to recreate it
it would be nice if we didn't have to use while, and the task was launched by the periodic timer once the interval expired, then the problem of the work taking more time than the actual interval won't be a problem
Those timers are funny until you need to scale your web API and run multiple pods of the same service. And when this is the case, only Hangfire or Quartz can help with proper scheduling.
This class is somehow senseless. Basically it aligns the runs on multiples of the interval. I never had the demand for such an behavior. Usually I wanted a) loop-runtime-compensation, where the Delay time is Max(0, interval-loopRuntime) or b) Delay a fixed amount of time during each run
I hate it when people use the Task output of an async method to fork execution and expect it to not block. In my opinion you should never do this, since it can lead to some very confusing behavior. You should really do a _timerTask = Task.Run(...), that would actually have the desired behavior and not block the main thread during Start(). In this example, your main thread is not freed until DoWorkAsync() actually awaits something, and how long is it going to take to do that? who knows... and then all of the sudden your application is hanging for some strange reason when it gets to the Start() line and you don't know why because you think that DoWorkAsync( ) is supposed to just return a Task and continue, when it is actually doing no such thing....
Nick, can you get some "Nick Specific" merch? I keep looking every few months to see & it's all boring af. If I'm going to drop some bank on a gym tank from someone I like it shouldn't look like I just came from the MS Store. I wanna see some Nick Chapsas: If you like this type of content & want to see more blarbagarbagoogooble Nick Chapsas: Make an array with some random numbers new int[] { 69, 420 } etc, etc
Firstly quartz is pretty archaic. Hangfire is a better solution. That being said there are many times where you just need a small timer and nothing else complicated so this is a better approach for that usecase
Pretty archaic, haven't heard that before. But I think some of that applies to your own advice? Like other streamers having a better solution? Just trying to push you towards having arguments 😉
6:20 😳😳😳 ohhh it is cheating, it tries to match the seconds by subtracting measured delays so your timer is not 1000, instead it is 1000 - x where x is delay/error from previous step.
I'm doing this for a small project and this EXACTLY what I've been looking for. Thank you!!!
For those wondering PeriodicTimer is in System.Threading namespace
Thank you bro!
System.Threading.Timer works like a metronome (doesn't wait for callback to end before invoking next one), exactly like PeriodicTimer.
so whats the difference?
@@-Chris_ What I see, API change. You get now possibility to write async code instead callbacks. It also rescue you from callback overlapping (if you don't consider it).
But you can set the AutoReset to false, and it will wait for the callback to end :)
This is useful in situation when your background task is lightweight/you can be sure your tasks finish in time. If you cannot be sure, the very first solution is better as it makes sure it does not overload your system. So, it really depends on what you want to solve, and at what cost (like instead of the first solution, does it worth using throttling?, etc.).
Hey Nick, you are reading my mind! I was thinking through such a thing just this weekend. You are doing great work for us all. Thank you and keep on streaming )))
How do you do this? Every time I work on something new, Bam! Out comes a Nick Chapsas video on it!
and now I rewrite the code I wrote today using PeriodicTimer instead of Timer.Timer. Merci!
This video meant great help. Thank you.
Task.Delay is fine when all you care is interval between runs
That being said, I can't use it to register and schedule multiple background services, is that correct?
@@PauloWirth you can, periodic timer will synchronise them allowing only one to do the job at any moment in time
@@mbalaganskiy I will try it out, I have an implementation using Cron expressions based on the IHostedService interface, which contains a System.Timers.Timer. I intend to test the PeriodicTimer implementation with a new version of my package.
_"PeriodicTimer is effectively a semaphore, allowing only one consumer to work"_ -- that's not how I read the documentation.
**"This timer is intended to be used only by a single consumer at a time: only one call to WaitForNextTickAsync(CancellationToken) may be in flight at any given moment."**
That sounds like the opposite of a semaphore to me. I.e. a semaphore is specifically designed to support two or more consumers blocking on the semaphore at a time, while PeriodicTimer is explicitly documented as supporting only one at a time.
@@harvey66616 You're actually correct. I've looked at the source code - there's a guard agains concurrent WaitForNextTickAsync. I've updated my comment.
This is what I do to get around the initial problem:
var delayTask = new Task.Delay(5000);
await DoWork();
await delayTask;
This gets around the missing milliseconds, as the delay starts before the work commences. Works fairly well... not sure if there's an eventual creep, but if you need a pretty good approx of every X seconds, real simple.
Note: at 5:26, the `!stoppingToken.IsCancellationRequest` condition should come before the call to `WaitForNextTickAsync`, as `WaitForNextTickAsync` throws a TaskCancelled exception when the CancellationSource cancels.
Thank you, I saw mention online of this new timer but did not see any good examples before. This made it simple.
The DispatcherTimer is for use with WPF applications, it schedules work on the Dispatcher thread of your application at a specific priority (WPF Dispatcher Priority, not thread priority). There are multiple priority levels, such as Background, Input, DataBind, Input, Normal, Render, etc. So for example, you could have an event that fires on the Dispatcher thread every 10 seconds at a DispatcherPriority.Background priority level, so that anything else running on the Dispatcher thread currently with a greater priority will take precedence.
It's fairly specialized, so while it's possible to replace the functionality using the PeriodicTimer using functions like Dispatcher.Yield, I don't think you'd need/want to in most cases where people are using a DispatcherTimer.
Thank you for sharing. Did not know this timer was introduced already. Looks handy
Brilliant, I used to use a calculated sleep to keep a steady tick. Perhaps that's all that it's doing.
just love it Nick ,exactly what I was looking for
I Love you Nick, I Always recommend your Channel at workspace.
I really like this timer. I have no idea how I missed it earlier in .NET 6
Dont forget that as you cancelled/disposed the CancellationTokenSource you will need to new one up in the Start() method if you are thinking of re-starting the same BackgroundTask instance
Thank you Nick. I just can't thank enough
I can actually use this. One service I wrote sends reminders to users and it checks whether or not it needs to on a daily basis. Used to be you'd write a windows service for such a thing and you might even do so today, but I wasn't prepared to go through that so I set up a scheduled task in Windows to open a web page every day. As you can guess, this is not ideal but now using this timer, I need only have 1 page open as long as I keep it open anyway. Maybe I can update that scheduled task to check if the window is open lol
Nick you make programming fun again!
In your console app, I believe if an Exception was thrown in the DoWorkAsync method, it would not get thrown until after the task was awaited, which would be after user hit enter and the await task.StopAsync ran
This is nice to know, and quite handy in some situations.
I've always done this by mod of time and compare against a previous which I'm guessing is what the tick is doing under the hood but nice to have a more idiomatic way of doing this...
Exactly. You don't just delay 1000ms. You can store start time, then delay (1000 - ElapsedMiliseconds). There are also other (more complex) ways to do this.
PeriodicTimer is great. I remember this workaround in the older days:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var sw = new Stopwatch();
while(!stoppingToken.IsCancellationRequested)
{
sw.Start();
// Some long running DoWorkAsync() task (~250ms)
await Task.Delay(250, stoppingToken);
// Wait for the rest of the second to elapse
await Task.Delay(1000 - sw.Elapsed.Milliseconds, stoppingToken);
sw.Reset();
}
}
(sorry slightly bad example, misplaced the `sw.Start()`, but you get what I meant!)
This was a great video. I'm going to watch it again.
Immediately got rid of my Timer.Timer timers to do background work and replaced with this. Not only is it more accurate, it simplified things being async.
when 5 timers is not enough =P, Thanks for video, I'll have a nice timer to play with now.
I have used the "Wrong Approach" many times.
It's not really wrong; I don't want it to loop at fixed intervals, I just want it to pause briefly between units of work, and for that it's fine.
Timer drift is an issue though, and there are again naive but good enough ways to deal with it:
If your waits are long, just adjust future sleep and do a catch-up (e.g. if you loop at 60s and the task took 5462ms, then sleep for 54538ms, and factor drift with a stopwatch).
If they're short, use a stopwatch to track how far behind schedule you are, and skip some when you're too far behind.
This Periodic Timer will skip ticks if the tasks takes too long; if Nick had paused for 1100ms, the task would've executed every 2s, so just be aware of that quirk if you use this.
Chances are you do not want loops piling on top of eachother so this is good.
@@hhcosminnet everything has a use case.
Love it. I’ve been hoping I could use this and your description of the console application looks like my use case.
Thanks Nick!
Nice topic! I use timer in net framework apps and always seemed to me that they are a pain in the ass to deal with. A timer that is awaitable looks like in in synch with the new task sintaxis! I'll be taking a closer look to this for sure.
What would be the correct way to have multiple timers in background service? Let's say I want two timers (one with 3s period and second with 5s). Should I fire inside ExecuteAsync two tasks where each handles its own timer and wait for both tasks to finish (when CTS is cancelled). Or going back to old timers with callbacks?
My main use case for a timer-like thing is a scheduler where I need the "DoWork" method executed at a precise time, i.e. every day at 11 a.m. So for that requirement I have a simple helper method called DelayUntil which calls "await Task.Delay" every 1000 ms until the expected time reached. I am not sure if it is the best practice for such a situation and if I can use this periodic timer to implement a better solution for my requirement but hey, it is simple and it works.
Depends on the implementation but it is not a bad practice as long as you don’t have something in the middle that can push the delay further back
@@nickchapsas And it doesn't chew up CPU cycles?
This was a fantastic one! Thanks!
For precise timing using the ExecuteAsync, the user should use Task.Delay using the time difference between the next DateTime and the current time (and therefore "guarantee" that timing is aligned e.g. per second).
Be aware that this is not necessarily precise and actually holds a potential surprise...
We were actually using a method like that, it seemed to work nicely. However when we ported it to Linux, the method sometimes was invoked several times every interval. Reason was that unlike what we saw ond windows, where the Task would awake with a slight delay, on linux it was awoken a bit early...
@@oM1naE I agree this is not precise, but this works better than simply delaying a constant value.
Awesome. Been using third party scheduling libraries to get the same effect. For the stuff I work on they're pretty overkill so this is great!
Was spending the day playing with quartz, totally agree this is pure
I prefer the first method. Its a lot more important to keep constant delay between operations than to execute them at exactly the same time of a second (or period)
This is awesome, Nick. Thank you for sharing.
Nice I just came across it a few days ago, really happ with it
@Nick thank for this awesome video on Timer. I have a question on point that you mentioned on 8:11 timeframe that when we have await in the loop then code does not continue. i wanted to understand problem of having await in the loop. Is it possible to explain it with an example. it will be help. Thanks.
Thanks!
You rock Nick. awesome stuff
Nice video :) what happens if an execption is thrown within the DoWorkAsync method? It won't be passed up, right? So it will repeat forever?
And I think you forgot to _timerTask = null in the StopAsync method, this is only needed for sure if you want to reuse this. Therefore you would also need a null check in the StartMethod
I've never seen this method of starting a new task, that is, by simply assigning the method call to a Task variable. Has this always been possible? Is it a shorthand for Task.StartNew() or have I missed out/misunderstood all this time?
Is this considered best practice in async programming for immediately executing a task on the thread pool from a method returning void?
It’s always been possible. This is a way to run it in the background without having to immediately await it
@@nickchapsas Thanks for clarifying! Strange I have never encountered this before, but (yet again) I learned something new watching your videos - thanks man! I can definitely see several use cases for this.
How does this compare to Hangfire? Can you replace Hangfire for basic cron jobs (database manipulation, fetching from apis, ...) by using this?
If I have a web app with a BackgroundService and PeriodicTimer and I want to change the timer interval, how do I do it without restarting the web app? Even if you inject IOptionsMonitor you have already initialised the service and timer - any ideas?
0:49 - The last timer is for WPF
Thanks for sharing. Could this be used in a MAUI application for syncing a SQLite database to a users cloud account on a user defined interval?
I guess... Perhaps use his sample class as a template and determine when it gets fired up. Being that MAUI supports the Hosting model that now permeates all .NET Core apps, you will get a lot of additional richness with it.
Hi. I use hangfire for background services. With this periodicTimer, how do u handle skipping concurrent jobs pls
Nice job, very cool
Thank you.
Excellent video. Even some great token work.
DispatchTimer is used for WPF
What happens if you miss the time? Like task was exectuted for 1.5 sec?
For small projects it can be enough, but for projects where u need advanced scheduling, multiple triggers, CRON's etc. it will not be used.
Is it possible to register multiple scheduled services using PeriodicTimer and run concurrently? Based on the documentation, it does not seem it is possible.
I'm curious as to what happens if the timer interval is shorter than the work operation. Do you get tightly packed back to back calls? Does it drop the calls until the next window elapses? Is there any way to guarantee a specific frequency or call rate?
As long as the duration of the execution is shorter than the delay there will be no call rate push back
I can see sometime people use recursion inside DoWork method. What's your opinion on this? Is using "while" loop is better approach?
where i cant find implementation for net framework 4.8?
How do you unit test a class that uses this? Do you wrap it in an interface?
Would this be appropriate for a heatbeat monitor app? We will potentially have hundreds (or even thousands) of IoT devices deployed that will be sending back heartbeat pings every X seconds and we need to raise an alert if we don't hear the heartbeat in the specified interval (if another heartbeat is heard we cancel the previous countdown to raising an alarm and set a new one). Would this type of timer be good for this scenario or is there a better way to do this?
i have a resource httpClient and i wait until other sync/download processes donot use this httpClient and then the caller can use this httpClient to post a new message. because of a loop inside that waits for the httpClient to be available i cannot use await. How should i code this, a sharing httpClient and somekind of semaphore to indicated that it is in use and the caller that wants to use httpClient should wait.
I like using reactive timers/delays and subscribe to them. No issues with tasks that take longer (since the timer is decoupled from the subscription) and very clean code. What are your thoughts?
Yup, I'm doing the same when using timers, but reactive is also meant to fix this old school events pattern, so I guess it's no surprise both solution work nicely.
I can't help but think the video miss a mention of IHostedService and the fact that you can add hosted services to a console app by using the generic host builder.
Sure, this solution works without the host builder, but if you want DI in a console app, why not use a host builder as well?
I might be missing the point though 🙂
The point is that people should know how to use a feature without depending on a completely separate one. Knowing how to do those things independendly expands your knowledge scope
In fact, sometimes you need a timer which doesn't count time duration took by the work, like the very first example in this video. Imagine what if the work takes more time than your timer duration. Your work queue will grow infinitely. Instead of that you want to wait 1 second - do the work whatever time it takes - wait 1 second - do the work - do the work whatever time it takes - and so on. Is there some new class for that?
Would it also be possible to put such a timer ind a web api to do some tasks at regular intervals?
Sure thing
where can i find the code source please ?
I find that usually I don't want something to happen periodically, as much as I want it to happen on a schedule... to that end, this kind of functionality is *very* rarely used for anything complicated. Maybe for some semi-low level things like reading from a memory channel and processing it in the background at certain intervals.
That's where I would store some configurable schedules somewhere and have the timer check to see what needs to be executed and spin it off in its own thread.
@@davidheale6435 I'd probably use a library :-P I'm lazy like that.
Why using Quartz or Hangfire is a bad decision when a timer operation is needed?
Why are events and handlers outdated? What should you use instead?
Rx or simple Actions/Functions
What with cases when timer should be created dynamically in various number ?
For example, one timer created per one request. Every action should by async, so input all timers to while condition (and use only one at the time) is not expected.
Why is Event Handling bad ?
silly question: does anyone know how does he set the output to print to the IDE's output window and not to the console, is it possible to do in VS2022 or only in that JetBrains IDE ?
one way is to use Debug.WriteLine instead of Console.WriteLine in a VS2022 console application. This will print the message to the IDE's output window.
Awesome!
Really nice! I'm curious how you would implement repeating tasks at a specific scheduled time?
You’d have to Task.Delay the TimeSpan which is the delta between now and the time that you want the timer to get triggered
@@nickchapsas is there something related with Cron expressions?
@@ruekkart quartz dotnet
I used NCrontab with a combination of.... what.... uh.. Nick just showed **not** to do . I'm now looking at implementing his approach. I had no idea a new timer was implemented.
@@nickchapsas Thanks for the answer. That's what I'm already doing, but I was curious if there was any other way, since it's, in the end, keeping the app running, which is really not a good thing for an API. The solution was to have a separate API to do theses scheduled tasks.
I belive you cannot Start and StopAsync task several times in your Console App setup as you call Dispose on your CancellationTokenSource in StopAsync. So you need to recreate it
it would be nice if we didn't have to use while, and the task was launched by the periodic timer once the interval expired, then the problem of the work taking more time than the actual interval won't be a problem
Anyone else get an XKCD 927 vibe from this?
Quartz is still unbeatable tbh
Thought the same. Why tinkering myself with this, when there is already such a solid and free solution.
Those timers are funny until you need to scale your web API and run multiple pods of the same service. And when this is the case, only Hangfire or Quartz can help with proper scheduling.
hell yeah
This class is somehow senseless. Basically it aligns the runs on multiples of the interval. I never had the demand for such an behavior. Usually I wanted
a) loop-runtime-compensation, where the Delay time is Max(0, interval-loopRuntime) or
b) Delay a fixed amount of time during each run
I think Coravel works quiet simpler and better than this approach. You can also prevent double executions.
I hate it when people use the Task output of an async method to fork execution and expect it to not block. In my opinion you should never do this, since it can lead to some very confusing behavior. You should really do a _timerTask = Task.Run(...), that would actually have the desired behavior and not block the main thread during Start(). In this example, your main thread is not freed until DoWorkAsync() actually awaits something, and how long is it going to take to do that? who knows... and then all of the sudden your application is hanging for some strange reason when it gets to the Start() line and you don't know why because you think that DoWorkAsync( ) is supposed to just return a Task and continue, when it is actually doing no such thing....
Nick, can you get some "Nick Specific" merch? I keep looking every few months to see & it's all boring af. If I'm going to drop some bank on a gym tank from someone I like it shouldn't look like I just came from the MS Store. I wanna see some
Nick Chapsas: If you like this type of content & want to see more blarbagarbagoogooble
Nick Chapsas: Make an array with some random numbers new int[] { 69, 420 }
etc, etc
Has anyone noticed that memory usage is constantly increasing when time is very short?
But why not Quartz?
Firstly quartz is pretty archaic. Hangfire is a better solution. That being said there are many times where you just need a small timer and nothing else complicated so this is a better approach for that usecase
@@nickchapsas Thanks for the answer!
Took a closer look to hangfire - pretty cool stuff
Pretty archaic, haven't heard that before. But I think some of that applies to your own advice? Like other streamers having a better solution? Just trying to push you towards having arguments 😉
A gazzillion ways to do everything…
You can achieve the same thing with a regular Stopwatch tbh
Example of losing a second (went from 15 to 17) on 3:44
You didn't Dispose timer, this is potentially harmful, if app won't be shutdown anyway.
6:20 😳😳😳 ohhh it is cheating, it tries to match the seconds by subtracting measured delays so your timer is not 1000, instead it is 1000 - x where x is delay/error from previous step.
1000 milliseconds. If only there were a more succinct way of writing that...
Thanks!
Thanks!
Thanks!