Run async method regularly with specified interval
The async
equivalent is a while
loop with Task.Delay
(which internally uses a System.Threading.Timer
):
public async Task PeriodicFooAsync(TimeSpan interval, CancellationToken cancellationToken)
{
while (true)
{
await FooAsync();
await Task.Delay(interval, cancellationToken)
}
}
It's important to pass a CancellationToken
so you can stop that operation when you want (e.g. when you shut down your application).
Now, while this is relevant for .Net in general, in ASP.Net it's dangerous to do any kind of fire and forget. There are several solution for this (like HangFire), some are documented in Fire and Forget on ASP.NET by Stephen Cleary others in How to run Background Tasks in ASP.NET by Scott Hanselman
Calling a method in specific interval
You can achieve that in multiple ways. One of the easiest one would be something like this:
public async Task RunScheduleJob(CancellationToken token)
{
while(!token.IsCancellationRequest)
{
YourMethod();
await Task.Delay(TimeSpan.FromHours(1), token)
}
}
PS. For such a simple job you don't need to use Reactive Extensions or any other external library. You can achieve it by just playing around with Task
instances
Run async hosted service every 5 minutes in ASP.NET Core
Use BackgroundService
instead of IHostedService
public class MyScheduler : BackgroundService
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public MyScheduler(IServiceScopeFactory serviceScopeFactory) => _serviceScopeFactory = serviceScopeFactory;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Option 1
while (!stoppingToken.IsCancellationRequested)
{
// do async work
using (var scope = _serviceScopeFactory.CreateScope())
{
var myService = scope.ServiceProvider.GetRequiredService<IMyService>();
await myService.Execute(stoppingToken);
}
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
}
// Option 2 (.NET 6)
var timer = new PeriodicTimer(TimeSpan.FromMinutes(5));
while (await timer.WaitForNextTickAsync(stoppingToken))
{
// do async work
// ...as above
}
}
}
Async sleep in loop
One way of doing it would be to create a Stopwatch and just wait for it to reach the next milestone after doing each operation.
The benefit of this way is that:
- You will add the overhead of the loop into the mix, adjusting your delay to compensate
- As such, it should have little to no "drifting" (where operations will get progressively "later" according to when they should've happened)
Sort of like this:
var sw = Stopwatch.StartNew();
long next = 100;
while (sw.ElapsedMilliseconds < 10000)
{
// do your operation here
long left = next - sw.ElapsedMilliseconds;
if (left > 0)
await Task.Delay((int)left);
next += 100;
}
Due to the accuracy of things, your operations will around each 100 milliseconds, but they shouldn't drift.
If you're OK with tying up a thread for the duration of those 10 seconds, you could switch out the delay part with Thread.Sleep
:
if (left > 0)
Thread.Sleep((int)left);
It will run your operations closer to the 100 millisecond mark, but might still have a slight overhead due to the fact that Thread.Sleep
has some overhead as well.
If you're OK with "burning CPU", you can do this instead:
var sw = Stopwatch.StartNew();
var next = 100;
while (sw.ElapsedMilliseconds < 10000)
{
// do your operation here
while (sw.ElapsedMilliseconds < next) ;
next += 100;
}
This will run your operation even closer to the actual 100 milliseconds mark, as Task.Delay
and Thread.Sleep
will have overhead that will tend to delay just slightly longer than what you intended. It will, however, tie up a CPU core for the duration of those 10 seconds.
Related Topics
C# VS Java Enum (For Those New to C#)
How to Deserialize Xml to Object
Cannot Delete Directory with Directory.Delete(Path, True)
Why Does My .Net 4 Application Know .Net 4 Is Not Installed
Inner Join of Datatables in C#
Call Asynchronous Method in Constructor
Delegate Keyword VS. Lambda Notation
Windows 10 Universal App File/Directory Access
{"<User Xmlns=''> Was Not Expected.} Deserializing Twitter Xml
Difference Between Invariantculture and Ordinal String Comparison
Most Elegant Way to Generate Prime Numbers
Printing All Contents of Array in C#
Compiling/Executing a C# Source File in Command Prompt
C# Splitting Strings on '#' Character
How to Split a String by a Multi-Character Delimiter in C#
Get Name of Property as a String