How can I set a timeout for ExecuteAsync of BackgroundService? - c#

I have a BackgroundService called Worker, which I override the ExecuteAsync method to run something each 10 seconds. Sometimes, what I run lasts very long. In this scenario, I want to kill what I am running, then rerun it. How can I achieve this?
My BackgroundService is as follows:
public class Worker : BackgroundService {
private readonly ITask task;
private readonly IHostApplicationLifetime appLifeTime;
public Worker(ITask task, IHostApplicationLifetime appLifeTime) {
this.task = task;
this.appLifeTime = appLifeTime;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
while (!stoppingToken.IsCancellationRequested)
{
try {
this.task.Execute(stoppingToken);
} catch (Exception e) {
this.appLifeTime.StopApplication();
}
await Task.Delay(10000, stoppingToken);
}
}
}
public interface ITask {
void Execute(CancellationToken stoppingToken);
}

You could do something along these lines:
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
var cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken);
cancellationSource.CancelAfter(10000);
return Task.Run(() => task.Execute(cancellationSource.Token));
}
If you want to handle execptions and/or await the task then you can but this is a simple example.

Related

System.Threading.Timer stuck for some time when server has load

We have below class members for timer:
private Timer _activityTimer;
Instantiating this timer variable in one method:
_activityTimer =
new Timer(async (timerState) => await UpdateActivityAsync(_ipAddress), null, new Random().Next(1, 15000), 15000);
But this did not calling periodically when server has load.
It is showing below log in serilog:
Starting HttpMessageHandler cleanup cycle with {InitialCount} items
Ending HttpMessageHandler cleanup cycle after {ElapsedMilliseconds}ms - processed: {DisposedCount} items - remaining: {RemainingItems} items
To handle async periodic callback, I would use System.Threading.PeriodicTimer. This way the execution of the next UpdateActivityAsync will not begin until the last one is done. If you still face thread pool starvation issue, you could manually create an additional thread in which you run the timer.
class Example {
private PeriodicTimer _activityTimer;
private IPAddress _ipAddress = IPAddress.Parse("192.168.1.1");
public void StartTimer() {
// Starting the timer, but not awaiting to not block the calling thread
// If you still face thread pool starvation issue, you could manually create an additional thread here
StartTimerLoopAsync();
}
public void StopTimer() {
_activityTimer.Dispose();
}
private async Task StartTimerLoopAsync() {
_activityTimer = new PeriodicTimer(TimeSpan.FromMilliseconds(15000));
while (await _activityTimer.WaitForNextTickAsync()) {
await UpdateActivityAsync(_ipAddress);
}
}
private async Task UpdateActivityAsync(IPAddress ipAddress) {
await Task.Delay(500); // Simulate some IO
Console.WriteLine(ipAddress);
}
}
UPDATE for .NET Core 3.1
Instead of PeriodicTimer you could simply use Task.Delay (it's not very accurate, but good enough for your use case I believe):
class Example {
private CancellationTokenSource _cancellationTokenSource = new ();
private IPAddress _ipAddress = IPAddress.Parse("192.168.1.1");
public void StartTimer() {
// Starting the timer, but not awaiting to not block the calling thread
// If you still face thread pool starvation issue, you could manually create an additional thread here
StartTimerLoopAsync();
}
public void StopTimer() {
_cancellationTokenSource.Cancel();
}
private async Task StartTimerLoopAsync() {
await Task.Delay(new Random().Next(1, 15000)); // System.Threading.Timer first delay
while (!_cancellationTokenSource.IsCancellationRequested) {
// run the Delay and UpdateActivityAsync simultaneously and wait for both
var delayTask = Task.Delay(15000, _cancellationTokenSource.Token);
await UpdateActivityAsync(_ipAddress, _cancellationTokenSource.Token);
await delayTask;
}
}
private async Task UpdateActivityAsync(IPAddress ipAddress, CancellationToken cancellationToken) {
await Task.Delay(1500, cancellationToken); // Simulate some IO
Console.WriteLine(ipAddress);
}
}
UPDATE
You could also create your own async Timer:
class Example : IDisposable {
private AsyncTimer<Example>? _timer;
public IPAddress IpAddress = IPAddress.Parse("192.168.1.1");
public void StartTimer() {
if (_timer is not null) {
return;
}
_timer = new AsyncTimer<Example>(async (state, ct) => await UpdateActivityAsync(state.IpAddress, ct), this, new Random().Next(1, 15000), 15000);
}
public void StopTimer() {
_timer?.Stop();
}
private async Task UpdateActivityAsync(IPAddress ipAddress, CancellationToken cancellationToken) {
await Task.Delay(500, cancellationToken); // Simulate some IO
Console.WriteLine(ipAddress);
}
public void Dispose() {
_timer?.Dispose();
}
}
class AsyncTimer<T> : IDisposable {
public delegate Task AsyncTimerDelegate(T state, CancellationToken cancellationToken);
private readonly CancellationTokenSource _cancellationTokenSource = new();
private readonly AsyncTimerDelegate _timerCallback;
private readonly TimeSpan _dueTime;
private readonly TimeSpan _interval;
private readonly T _state;
public AsyncTimer(AsyncTimerDelegate timerCallback, T state, int dueTime, int interval) {
_timerCallback = timerCallback;
_state = state;
_dueTime = TimeSpan.FromMilliseconds(dueTime);
_interval = TimeSpan.FromMilliseconds(interval);
// Starting the timer, but not awaiting to not block the calling thread
// If you still face thread pool starvation issue, you could manually create an additional thread here
StartTimerLoopAsync();
}
public void Stop() {
_cancellationTokenSource.Cancel();
}
private async Task StartTimerLoopAsync() {
await Task.Delay(_dueTime);
while (!_cancellationTokenSource.IsCancellationRequested) {
// run the Delay and UpdateActivityAsync simultaneously and wait for both
var delayTask = Task.Delay(_interval, _cancellationTokenSource.Token);
await _timerCallback.Invoke(_state, _cancellationTokenSource.Token);
await delayTask;
}
}
public void Dispose() {
_cancellationTokenSource.Dispose();
}
}

How To Send A Cancellation Token To A Background Service In Blazor Server

I am trying to add a start and stop functionality to a long-running background service in my Blazor server app by the press of a button(s). The background service, PeriodicExecutor, is started without any user interaction in the startup.cs file and works as expected:
startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddHostedService<PeriodicExecutor>();
}
Below is the essential code in the background service:
PeriodicExecutor.cs
public class PeriodicExecutor : BackgroundService
{
protected override async Task ExecuteAsync(System.Threading.CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(5000);
}
}
}
Below is the essential code in the base class that handles the code for the button onclick() events:
IndexBase.cs
public class IndexBase : ComponentBase
{
[Inject]
IJSRuntime JSRuntime { get; set; }
protected override void OnInitialized()
{
}
protected async Task StartService()
{
if (!PeriodicExecutor.status)
{
await JSRuntime.InvokeVoidAsync("alert", "Microservice Started");
}
else
{
await JSRuntime.InvokeVoidAsync("alert", "Microservice Already Running");
}
}
protected async Task StopService()
{
if (PeriodicExecutor.status)
{
await JSRuntime.InvokeVoidAsync("alert", "Microservice Stopped");
}
else
{
await JSRuntime.InvokeVoidAsync("alert", "Microservice Already Stopped");
}
}
}
So the question is, how can i stop the backgroundservice with a cancellation token from the StopService() method and start the background service in the StartService() method? Any detailed answers would be greatly appreciated.

What's the efficient alternative to setInterval done completely in Blazor Webassembly?

I was thinking of something like:
protected override async Task OnInitializedAsync() {
//...
RunMeEndlesslyWithoutAwait();
//...
}
protected async Task RunMeEndlesslyWithoutAwait() {
while (online) {
//... do stuff
await Task.Delay(60000);
}
}
but I'm not sure if it's the most adeguage.
Is there any known best/efficient ways to the JS function setInterval(...) that uses blazor webassembly?
You are probably looking for a Timer
#using System.Timers
#code
{
Timer timer = new Timer();
protected override Task OnInitializedAsync()
{
timer.Interval = 6000;
timer.Elapsed +=async(_, _) => await RunMeEndlesslyWithoutAwait();
}
}
It would be better if you use PeriodicTimer as it is new and fancy way to create a backgroud task.
Please note that it is available with .NET 6.
Here is a sample of usage:
public class BackgroundTask
{
private Task? _timerTask;
private readonly PeriodicTimer _timer;
private readonly CancellationTokenSource _cts= new();
public BackgroundTask(TimeSpan interval)
{
_timer = new(interval);
}
public void Start()
{
_timerTask = RunAsync();
}
private async Task RunAsync()
{
try
{
while (await _timer.WaitForNextTickAsync(_cts.Token))
{
Console.WriteLine($"Task worked: {DateTime.Now:O}");
}
}
catch (OperationCanceledException)
{
}
}
public async Task StopAsync()
{
if (_timerTask is null)
{
return;
}
_cts.Cancel();
await _timerTask;
_cts.Dispose();
Console.WriteLine("Task has just been stopped.");
}
You call it like this:
BackgroundTask task = new BackgroundTask(TimeSpan.FromSeconds(1));
task.Start();

How to gracefully capture exceptions from timer events?

In an ASP Net Core 2 MVC app, I am using this BackGroundService (via IHostingService) with the below implementation to update singleton gauge objects for a real-time dashboard. However, I don't know of a good way to ensure I capture any exceptions thrown when the Update event is fired.
Note: I am aware of AppDomain.UnhandledException but find it to be more of a sledgehammer approach and would like something easier to maintain and scale.
Or, is there an entirely better way to periodically update data in a background task in ASP.NET Core 2?
public class GaugeUpdater : BackgroundService
{
private readonly List<IUpdateable> _updatables;
private Timer _timer;
public GaugeUpdater (IEnumerable<IUpdateable> updateables)
{
_updatables = updateables.ToList();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
if (!stoppingToken.IsCancellationRequested)
{
await InitializeUpdateables();
SetTimer();
}
}
private void SetTimer()
{
const int intervalMilliseconds = 60_000;
var interval = new TimeSpan(0, 0, 0, 0, intervalMilliseconds);
_timer = new Timer(UpdateAll, null, interval, interval);
}
private async Task InitializeUpdateables()
{
var tasks = _updatables.Select(x => x.Initialize()).ToList();
await Task.WhenAll(tasks);
}
private async void UpdateAll(object state)
{
// TODO: Find way to handle exceptions, as awaiting async void makes it impossible for caller to catch.
// AppDomain.UnhandledException is possible but hard to maintain and handle in this scope.
_updatables.ForEach(async x => await x.Update());
}
}
async void can work for event handlers. Create and event and raise it with the timer. From there you should be able to await async tasks and handle exceptions
public class GaugeUpdater : BackgroundService {
private readonly List<IUpdateable> _updatables;
private Timer _timer;
public GaugeUpdater (IEnumerable<IUpdateable> updateables) {
_updatables = updateables.ToList();
Updating += OnUpdating; //Subscribe to event
}
private event EventHandler Updating = delegate { };
private async void OnUpdating(object sender, EventArgs args) {
try {
var tasks = _updatables.Select(x => x.Update());
await Task.WhenAll(tasks);
} catch {
//TODO: Logging???
}
}
private void UpdateAll(object state) {
Updating(this, EventArgs.Empty); //Raise event
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
if (!stoppingToken.IsCancellationRequested) {
await InitializeUpdateables();
SetTimer();
}
}
private void SetTimer() {
const int intervalMilliseconds = 60_000;
var interval = new TimeSpan(0, 0, 0, 0, intervalMilliseconds);
_timer = new Timer(UpdateAll, null, interval, interval);
}
private async Task InitializeUpdateables() {
var tasks = _updatables.Select(x => x.Initialize()).ToList();
await Task.WhenAll(tasks);
}
}

How to ensure a small UnitOfWork job is completed on a windows service Stop or Shutdown

I have a job that run every X seconds that shouldn't take long to execute (less than 5 seconds) that is ran in a WindowsService. My goal is to ensure that a currently running job will complete it's execution if the Windows Service is stopped in the middle of it's execution.
This thread seemed interesting, but it doesn't work so far:
Task Handling on Application Shutdown
Any help will be appreciated.
WindowsService implementation:
public partial class WindowsService : ServiceBase
{
private readonly ILogger logger;
private readonly IStartJob startJob;
private readonly CancellationTokenSource cancellationTokenSource;
private Task task;
public WindowsService(IStartJob startJob, ILogger logger)
{
// Dependancy injection of logger and JobStarter
this.startJob = startJob;
this.logger = logger;
InitializeComponent();
cancellationTokenSource = new CancellationTokenSource();
}
protected override void OnStart(string[] args)
{
task = new Task(() => startJob.Start(cancellationTokenSource.Token),
cancellationTokenSource.Token, TaskCreationOptions.LongRunning);
task.Start();
logger.Info("Service started");
}
protected override void OnStop()
{
RequestAdditionalTime(TimeSpan.FromSeconds(30).Milliseconds);
cancellationTokenSource.Cancel();
try { task.Wait(); }
catch (Exception ex) { logger.Error("Error while stopping the Service", ex); }
logger.Info("Service stopped");
}
}
StartJob Implementation:
public class StartJob : IStartJob
{
private readonly ILogger logger;
private readonly IExecuteJob executeJob;
private DateTime lastExecution = DateTime.UtcNow.AddDays(-1);
public StartJob(ILogger logger, IExecuteJob executeJob)
{
this.logger = logger;
this.executeJob = executeJob;
}
public async void Start(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
var nextExecution = lastExecution.AddSeconds(5);
if (nextExecution < DateTime.UtcNow)
{
try
{
logger.Info("Start Job Execution");
// To similate long process not ended, break point here
// and stop the service before continuing the execution.
Thread.Sleep(5000);
executeJob.Execute();
logger.Info("Job completed");
AdjustLastExecution(nextExecution);
}
catch (Exception ex)
{
logger.Error("Unexpected exception while executing job", ex);
}
}
// Would expect the cancelToken to activate here or in the while condition
await Task.Delay(TimeSpan.FromSeconds(1), token);
}
}
private void AdjustLastExecution(DateTime nextExecution)
{
// To ensure we have an heartbeat close to 5 seconds
lastExecution = nextExecution.AddSeconds(5) > DateTime.UtcNow ?
nextExecution : DateTime.UtcNow.AddSeconds(-5);
}
}
I tried a bunch of variations with the cancelToken present in the entry task, task wait and wait millisecondsTimeout, but nothing did the trick so far.
I attached my solution to the executing WindowsService and ensure that the executing job would be in the middle of it with break points and Thread.Sleep. But the Job completed log isn't written and the attached processed is dropped before it.
I shortly looked at your code and only thing I would try is
to replace :
async void
with
async Task
I don't guarantee that it applies to your case, but I recalled reading this article recommending to avoid async void.
In C#, async void methods are a scourge upon your code..
Haacked.com | Avoid async void methods
EDIT :
task = new Task(() => startJob.Start(cancellationTokenSource.Token),
cancellationTokenSource.Token, TaskCreationOptions.LongRunning);
with
task = startJob.Start(cancellationTokenSource.Token);

Categories

Resources