I'm writing a hosted service in .Net-Core which runs a job in the background based off of a timer.
Currently I have to code running synchronously like so:
public override Task StartAsync(CancellationToken cancellationToken)
{
this._logger.LogInformation("Timed Background Service is starting.");
this._timer = new Timer(ExecuteTask, null, TimeSpan.Zero,
TimeSpan.FromSeconds(30));
return Task.CompletedTask;
}
private void ExecuteTask(object state)
{
this._logger.LogInformation("Timed Background Service is working.");
using (var scope = _serviceProvider.CreateScope())
{
var coinbaseService = scope.ServiceProvider.GetRequiredService<CoinbaseService>();
coinbaseService.FinalizeMeeting();
}
}
I'd like to run this Async on the timer but I don't want to run async using fire and forget because my it could cause race conditions in my code.
e.g( subscribing to the timer.Elapsed
event)
Is there a way I can leverage asynchronous code on a timed schedule without executing fire and forget
The whole purpose of
async
is to not hold up the primary threads. But this is a background thread already, so it doesn't really matter - unless it's an ASP.NET Core application. That's the only time it would matter since there is a limited thread pool and exhausting it means that no more requests can be served.If you really want to run it
async
, just make itasync
:Yes, I know you say you don't want to "fire and forget", but events really are just that: they're fire and forget. So your
ExecuteTask
method will be called and nothing will care (or check) if it's (1) still running or (2) if it failed. That is true whether you run thisasync
or not.You can mitigate failures by just wrapping everything your
ExecuteTask
method in atry
/catch
block and make sure it's logged somewhere so you know what happened.The other issue is knowing if it's still running (which, again, is a problem even if you're not running
async
). There is a way to mitigate that too:In this case, your timer just starts the task. But the difference is that you're keeping a reference to the
Task
. This would let you check on the status of theTask
anywhere else in your code. For example, if you want to verify whether it's done, you can look atdoWorkTask.IsCompleted
ordoWorkTask.Status
.Additionally, when your application shuts down, you can use:
to make sure the task has completed before closing your application. Otherwise, the thread would just be killed, possibly leaving things in an inconsistent state. Just be aware that using
await doWorkTask
will throw an exception if an unhandled exception happened inDoWork()
.It's also probably a good idea to verify if the previous task has completed before starting the next one.
For those who are looking for complete example which prevents running tasks concurrently. Based on @Gabriel Luci answer and comments.
Please feel free to comment so I can correct it.