I create the periodic timer which run under background service
public class PeriodicHostedService : BackgroundService
{
private readonly TimeSpan period = TimeSpan.FromSeconds(1);
private readonly ILogger<PeriodicHostedService> logger;
private readonly IServiceScopeFactory factory;
private int executionCount = 0;
public PeriodicHostedService(ILogger<PeriodicHostedService> logger, IServiceScopeFactory factory)
{
this.logger=logger;
this.factory=factory;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using PeriodicTimer timer = new(period);
using var scope = factory.CreateScope();
ITimerJob job = scope.ServiceProvider.GetRequiredService<ITimerJob>();
while (
!stoppingToken.IsCancellationRequested &&
await timer.WaitForNextTickAsync(stoppingToken))
{
try
{
await job.ProcessAsync();
executionCount++;
logger.LogInformation($"Executed PeriodicHostedService - Count: {executionCount}");
}
catch (Exception ex)
{
logger.LogInformation($"Failed to execute PeriodicHostedService with exception message {ex.Message}. Good luck next round!");
}
}
}
}
I have set the timer run every second
however, I have job in timer need to run over 1 second just an example
internal class TimerJob : ITimerJob
{
private int runningID;
public async Task ProcessAsync()
{
runningID++;
Console.WriteLine($"{DateTime.Now} > Current Running ID : {runningID}");
await LongTimeJob();
}
private async Task LongTimeJob ()
{
Console.WriteLine($"{DateTime.Now} > Step1 Async Job End ID : {runningID}");
await Task.Delay(3000).ConfigureAwait(false);
}
}
can I know how to write the timer which force to execute on every second (and let longtime job continue work)
Thank you
you can chose not to await the job.ProcessAsync() which would allow your code to continue waiting for the next tick.
_ = job.ProcessAsync();
I must admit, running jobs every minute that are likely to run long might become a resource hog eventually. You should check your design for any unwanted side effects.
Related
I have a BackgroundService that I start from an API Controller.
There should never be more than one BackgroundService running.
How can I check if a job is already running? So I don't start a new?
API to start a new job and related code
[HttpPost]
public async Task<IActionResult> RunJob(JobMessage msg)
{
if (_queue.Count > 0)
{
return StatusCode(429, "DocumentDistributor are running. Try again later");
}
await _queue.Queue(msg);
return Ok("DocumentDistributor will start in about one minute.");
}
public interface IBackgroundTaskQueue
{
Task Queue(JobMessage message);
Task<JobMessage> Dequeue();
public int Count { get; }
}
public sealed class QueuedHostedService : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
public QueuedHostedService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
using var scope = _serviceProvider.CreateScope();
var calculator = scope.ServiceProvider.GetRequiredService<QueueDocumentDistributor>();
await calculator.RunService();
}
catch (OperationCanceledException)
{
// Prevent throwing if the Delay is cancelled
}
catch (Exception e)
{
Log.Error(e, "Error in QueuedHostedService");
}
// check queue every 1 minute
await Task.Delay(1000 * 60, stoppingToken);
}
}
}
public class QueueDocumentDistributor
{
private readonly IBackgroundTaskQueue _queue;
private readonly ReportService _service;
public QueueDocumentDistributor(IBackgroundTaskQueue queue, ReportService service)
{
_queue = queue;
_service = service;
}
public async Task RunService()
{
var message = await _queue.Dequeue();
if (message == null) return;
await _service.CreateReports(message);
}
}
AddHostedService adds singleton instance of IHostedService, so if there is no parallel processing in the implementation framework guarantees the single job execution.
There should never be more than one BackgroundService running.
There will be only single instance of background service per type running.
How can I check if a job is already running?
Depends on what do you mean by "job". If BackgroundService - then it is started by the framework. If your some custom payload in queue - then you will need to implement some monitoring manually.
So I don't start a new?
You don't start (usually) background service manually. If QueueDocumentDistributor.RunService gurantees single execution of your logic at a time - your are fine.
Based on provided implementation looks like a single queue element is processed at a time.
Read more:
Background tasks with hosted services in ASP.NET Core
I have a SignalR app in DotNet 3.1, kind-of a large chat app, and I am trying to add two BackgroundServices.
The BackgroundServices are setup to run for as long as the ASP.NET app runs.
The first BackgroundService has a very fast main loop (50 ms) and seems to work well.
The second BackgroundService has a much longer main loop (1000 ms) and seems to start randomly, stop executing randomly, and then re-starts executing again ... randomly. It is almost like the second bot is going to sleep, for a long period of time (30 to 90 seconds) and then wakes up again with the object state preserved.
Both BackgroundServices have the same base code with different Delays.
Is it possible to have multiple, independent, non-ending, BackgroundServices? If so, then what am I doing wrong?
I have the services registered like this ...
_services.AddSimpleInjector(_simpleInjectorContainer, options =>
{
options.AddHostedService<SecondaryBackgroundService>();
options.AddHostedService<PrimaryBackgroundService>();
// AddAspNetCore() wraps web requests in a Simple Injector scope.
options.AddAspNetCore()
// Ensure activation of a specific framework type to be created by
// Simple Injector instead of the built-in configuration system.
.AddControllerActivation()
.AddViewComponentActivation()
.AddPageModelActivation()
.AddTagHelperActivation();
});
And I have two classes (PrimaryBackgroundService/SecondaryBackgroundService) that have this ...
public class SecondaryBackgroundService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
await Task.Factory.StartNew(async () =>
{
// loop until a cancalation is requested
while (!cancellationToken.IsCancellationRequested)
{
//await Task.Delay(TimeSpan.FromMilliseconds(50), cancellationToken);
await Task.Delay(TimeSpan.FromMilliseconds(1000), cancellationToken);
try
{
await _doWorkDelegate();
}
catch (Exception ex)
{
}
}
}, cancellationToken);
}
}
Should I setup a single BackgroundService that spins off two different Tasks; in their own threads? Should I be using IHostedService instead?
I need to make sure that the second BackgroundService runs every second. Also, I need to make sure that the second BackgroundService never impacts the faster running primary BackgroundService.
UPDATE:
I changed the code to use a Timer, as suggested, but now I am struggling with calling an async Task from a Timer event.
Here is the class I created with the different options that work and do not work.
// used this as the base: https://github.com/aspnet/Hosting/blob/master/src/Microsoft.Extensions.Hosting.Abstractions/BackgroundService.cs
public abstract class RecurringBackgroundService : IHostedService, IDisposable
{
private Timer _timer;
protected int TimerIntervalInMilliseconds { get; set; } = 250;
// OPTION 1. This causes strange behavior; random starts and stops
/*
protected abstract Task DoRecurringWork();
private async void OnTimerCallback(object notUsedTimerState) // use "async void" for event handlers
{
try
{
await DoRecurringWork();
}
finally
{
// do a single call timer pulse
_timer.Change(this.TimerIntervalInMilliseconds, Timeout.Infinite);
}
}
*/
// OPTION 2. This causes strange behavior; random starts and stops
/*
protected abstract Task DoRecurringWork();
private void OnTimerCallback(object notUsedTimerState)
{
try
{
var tf = new TaskFactory(System.Threading.CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
tf.StartNew(async () =>
{
await DoRecurringWork();
})
.Unwrap()
.GetAwaiter()
.GetResult();
}
finally
{
// do a single call timer pulse
_timer.Change(this.TimerIntervalInMilliseconds, Timeout.Infinite);
}
}
*/
// OPTION 3. This works but requires the drived to have "async void"
/*
protected abstract void DoRecurringWork();
private void OnTimerCallback(object notUsedTimerState)
{
try
{
DoRecurringWork(); // use "async void" in the derived class
}
finally
{
// do a single call timer pulse
_timer.Change(this.TimerIntervalInMilliseconds, Timeout.Infinite);
}
}
*/
// OPTION 4. This works just like OPTION 3 and allows the drived class to use a Task
protected abstract Task DoRecurringWork();
protected async void DoRecurringWorkInternal() // use "async void"
{
await DoRecurringWork();
}
private void OnTimerCallback(object notUsedTimerState)
{
try
{
DoRecurringWork();
}
finally
{
// do a single call timer pulse
_timer.Change(this.TimerIntervalInMilliseconds, Timeout.Infinite);
}
}
public virtual Task StartAsync(CancellationToken cancellationToken)
{
// https://stackoverflow.com/questions/684200/synchronizing-a-timer-to-prevent-overlap
// do a single call timer pulse
_timer = new Timer(OnTimerCallback, null, this.TimerIntervalInMilliseconds, Timeout.Infinite);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
try { _timer.Change(Timeout.Infinite, 0); } catch {; }
return Task.CompletedTask;
}
public void Dispose()
{
try { _timer.Change(Timeout.Infinite, 0); } catch {; }
try { _timer.Dispose(); } catch {; }
}
}
Is OPTION 3 and/or OPTION 4 correct?
I have confirmed that OPTION 3 and OPTION 4 are overlapping. How can I stop them from overlapping? (UPDATE: use OPTION 1)
UPDATE
Looks like OPTION 1 was correct after all.
Stephen Cleary was correct. After digging and digging into the code I did find a Task that was stalling the execution under the _doWorkDelegate() method. The random starts and stops was caused by an HTTP call that was failing. Once I fixed that (with a fire-and-forget) OPTION 1 started working as expected.
I would recommend writing two timed background tasks as shown in the documentation
Timed background tasks documentation
then they are independent and isolated.
public class PrimaryBackgroundService : IHostedService, IDisposable
{
private readonly ILogger<PrimaryBackgroundService> _logger;
private Timer _timer;
public PrimaryBackgroundService(ILogger<PrimaryBackgroundService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("PrimaryBackgroundService StartAsync");
TimeSpan waitTillStart = TimeSpan.Zero;
TimeSpan intervalBetweenWork = TimeSpan.FromMilliseconds(50);
_timer = new Timer(DoWork, null, waitTillStart, intervalBetweenWork);
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("PrimaryBackgroundService DoWork");
// ... do work
}
public Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("PrimaryBackgroundService is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
create the SecondaryBackgroundService using similar code and register them as you did before
options.AddHostedService<SecondaryBackgroundService>();
options.AddHostedService<PrimaryBackgroundService>();
Note that if you want to use any dependency injection then you have to inject IServiceScopeFactory into the background service constructor and call scopeFactory.CreateScope()
I am creating a worker service which will be run as a windows service. I have a requirement where I would like to invoke two tasks which may have different timers.
Say DoWork should be called every 5 minutes and DoAnotherWork should be called every 10 minutes or so. These two tasks can run in parallel and are not dependant on each other.
I was able to create task DoWork which can run after every 5 minutes. I am a bit confused about how to implement another task that will have different timer duration?
public class Worker : BackgroundService
{
private readonly IServiceScopeFactory _scopeFactory;
private IDataLoaderService _dataLoaderService;
public override Task StartAsync(CancellationToken cancellationToken)
{
using var scope = _scopeFactory.CreateScope();
_dataLoaderService = scope.ServiceProvider.GetRequiredService<IDataLoaderService>();
return base.StartAsync(cancellationToken);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await DoWork(stoppingToken, _dataLoaderService);
await Task.Delay(300000, stoppingToken); //Run every 5 minutes
await DoAnotherWork(stoppingToken, _dataLoaderService);
await Task.Delay(600000, stoppingToken); //Run every 10 minutes
}
}
private async Task DoWork(CancellationToken stoppingToken, IDataLoaderService loaderService)
{
await loaderService.Process();
}
private async Task DoAnotherWork(CancellationToken stoppingToken, IDataLoaderService loaderService)
{
await loaderService.Validate();
}
}
These two tasks can run in parallel and are not dependant on each other.
Sounds to me like you have two services:
public class ProcessDataLoaderWorker : BackgroundService
{
private readonly IServiceScopeFactory _scopeFactory;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using var scope = _scopeFactory.CreateScope();
var dataLoaderService = scope.ServiceProvider.GetRequiredService<IDataLoaderService>();
while (true)
{
await dataLoaderService.Process();
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken); //Run every 5 minutes
}
}
}
public class ValidateDataLoaderWorker : BackgroundService
{
private readonly IServiceScopeFactory _scopeFactory;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using var scope = _scopeFactory.CreateScope();
var dataLoaderService = scope.ServiceProvider.GetRequiredService<IDataLoaderService>();
while (true)
{
await dataLoaderService.Validate();
await Task.Delay(TimeSpan.FromMinutes(10), stoppingToken); //Run every 10 minutes
}
}
}
I also modified the way the IDataLoaderService was used so that it is not used outside its scope, and changed the Task.Delay arguments to be more self-explanatory.
If you don't want to use existing scheduling libraries in your case you can go with having two timers, like in this docs, where System.Threading.Timer is utilized. Something like that:
public class Worker : IHostedService, IDisposable
{
private readonly IServiceScopeFactory _scopeFactory;
private IDataLoaderService _dataLoaderService;
private Timer _timer1;
private Timer _timer2;
public Task StartAsync(CancellationToken cancellationToken)
{
_dataLoaderService = scope.ServiceProvider.GetRequiredService<IDataLoaderService>();
_timer1 = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(300));
_timer2 = new Timer(DoAnotherWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(600));
return Task.CompletedTask;
}
private async void DoWork(object _)
{
// or create scope and resolve here
await _loaderService.Process();
}
private async void DoAnotherWork(object _)
{
// or create scope and resolve here
await _loaderService.Validate();
}
public Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Timed Hosted Service is stopping.");
_timer1?.Change(Timeout.Infinite, 0);
_timer2?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer1?.Dispose();
_timer2?.Dispose();
}
}
I have a complex situation but I will try to short it out and let only know for important details. I am trying to implement a task-based job handling. here is the class for that:
internal class TaskBasedJob : IJob
{
public WaitHandle WaitHandle { get; }
public JobStatus Status { get; private set; }
public TaskBasedJob(Func<Task<JobStatus>> action, TimeSpan interval, TimeSpan delay)
{
Status = JobStatus.NotExecuted;
var semaphore = new SemaphoreSlim(0, 1);
WaitHandle = semaphore.AvailableWaitHandle;
_timer = new Timer(async x =>
{
// return to prevent duplicate executions
// Semaphore starts locked so WaitHandle works properly
if (semaphore.CurrentCount == 0 && Status != JobStatus.NotExecuted)
{
return;
Status = JobStatus.Failure;
}
if(Status != JobStatus.NotExecuted)
await semaphore.WaitAsync();
try
{
await action();
}
finally
{
semaphore.Release();
}
}, null, delay, interval);
}
}
Below is the scheduler class :
internal class Scheduler : IScheduler
{
private readonly ILogger _logger;
private readonly ConcurrentDictionary<string, IJob> _timers = new ConcurrentDictionary<string, IJob>();
public Scheduler(ILogger logger)
{
_logger = logger;
}
public IJob ScheduleAsync(string jobName, Func<Task<JobStatus>> action, TimeSpan interval, TimeSpan delay = default(TimeSpan))
{
if (!_timers.ContainsKey(jobName))
{
lock (_timers)
{
if (!_timers.ContainsKey(jobName))
_timers.TryAdd(jobName, new TaskBasedJob(jobName, action, interval, delay, _logger));
}
}
return _timers[jobName];
}
public IReadOnlyDictionary<string, IJob> GetJobs()
{
return _timers;
}
}
Inside of this library I have a service like below: So the idea of this service is only to fetch some data at the dictionary called _accessInfos and its async method. You can see at the constructor I already add the job to fetch the data.
internal class AccessInfoStore : IAccessInfoStore
{
private readonly ILogger _logger;
private readonly Func<HttpClient> _httpClientFunc;
private volatile Dictionary<string, IAccessInfo> _accessInfos;
private readonly IScheduler _scheduler;
private static string JobName = "AccessInfoProviderJob";
public AccessInfoStore(IScheduler scheduler, ILogger logger, Func<HttpClient> httpClientFunc)
{
_accessInfos = new Dictionary<string, IAccessInfo>();
_config = config;
_logger = logger;
_httpClientFunc = httpClientFunc;
_scheduler = scheduler;
scheduler.ScheduleAsync(JobName, FetchAccessInfos, TimeSpan.FromMinutes(1));
}
public IJob FetchJob => _scheduler.GetJobs()[JobName];
private async Task<JobStatus> FetchAccessInfos()
{
using (var client = _httpClientFunc())
{
accessIds = //calling a webservice
_accessInfos = accessIds;
return JobStatus.Success;
}
}
All of this code is inside another library that I have referenced into my ASP.NET Core 2.1 project. On the startup class I have a call like this:
//adding services
...
services.AddScoped<IScheduler, Scheduler>();
services.AddScoped<IAccessInfoStore, AccessInfoStore>();
var accessInfoStore = services.BuildServiceProvider().GetService<IAccessInfoStore>();
accessInfoStore.FetchJob.WaitHandle.WaitOne();
At the first time WaitOne() method does not work so the data are not loaded(_accessInfos is empty) but if I refresh the page again I can see the data loaded(_accessInfos is not empty but has data). So, as far as I know WaitOne() method is to block thread execution until my job is completed.
Does anybody know why WaitOne() method does not work properly or what I might be doing wrong ?
EDIT 1:
Scheduler only stores all IJob-s into a concurrent dictionary in order to get them later if needed mainly for showing them in a health page. Then every time we insert a new TaskBasedJob in dictionary the constructor will be executed and at the end we use a Timer to re-execute the job later after some interval, but in order to make this thread-safe I use SemaphoreSlim class and from there I expose WaitHandle. This is only for those rare cases I need to turn a method from async to sync. Because in general I would not use this because the job will execute in async manner for normal cases.
What I expect - The WaitOne() should stop execution of current thread and wait until my scheduled job is executed and then continue on executing current thread. In my case current thread is the one running Configure method at StartUp class.
Colleague of Rajmond here. I figure out our issue. Basically, waiting works fine and so on. Our issue is simply that if you do IServiceCollection.BuildServiceProvider() you will get a different scope each time (and thus a different object is created even with Singleton instance). Simple way to try this out:
var serviceProvider1 = services.BuildServiceProvider();
var hashCode1 = serviceProvider1.GetService<IAccessInfoStore>().GetHashCode();
var hashCode2 = serviceProvider1.GetService<IAccessInfoStore>().GetHashCode();
var serviceProvider2 = services.BuildServiceProvider();
var hashCode3 = serviceProvider2.GetService<IAccessInfoStore>().GetHashCode();
var hashCode4 = serviceProvider2.GetService<IAccessInfoStore>().GetHashCode();
hashCode1 and hashCode2 are the same, same as hashCode3 and hashCode4 (because Singleton), but hashCode1/hashCode2 are not the same as hashCode3/hashCode4 (because different service provider).
The real fix will probably be some check in that IAccessInfoStore that will block internally until the job has finished the first time.
Cheers!
I want to run two tasks simultaneously, with one having a Task.Delay() in it.
i.e. one runs continuously and one runs every 15 minutes.
Here's what I have so far:
public class ContinousAndAggregatedCheckRunner<T, T2>
{
private readonly int _aggregationInterval;
private readonly List<T> _collectedData;
private readonly Func<IEnumerable<T>, Task<T2>> _aggregator;
private readonly Func<Task<IEnumerable<T>>> _collector;
private CancellationToken _aggregationToken = default(CancellationToken);
private CancellationToken _collectionToken = default(CancellationToken);
public ContinousAndAggregatedCheckRunner(Func<IEnumerable<T>, Task<T2>> aggregator,
int aggregationInterval,
Func<Task<IEnumerable<T>>>)
{
_aggregator = aggregator;
_aggregationInterval = aggregationInterval;
_collector = collector;
_collectedData = new List<T>();
}
public async Task Run()
{
Task.WaitAll(Collect(), Aggregate());
}
private async Task Collect()
{
while (!_collectionToken.IsCancellationRequested)
{
Console.WriteLine($"Collecting {DateTime.Now.ToLongDateString()} {DateTime.Now.ToLongTimeString()}");
try
{
var results = await _collector();
_collectedData.AddRange(results);
}
catch (TaskCanceledException)
{
break;
}
}
}
private async Task Aggregate()
{
while (!_aggregationToken.IsCancellationRequested)
{
Console.WriteLine("Aggregating");
try
{
var aggregate = await _aggregator(_collectedData);
var taskFactory = new TaskFactory();
await taskFactory.StartNew(() => Send(aggregate), _aggregationToken);
_collectedData.Clear();
await Task.Delay(TimeSpan.FromMinutes(_aggregationInterval), _aggregationToken);
}
catch (TaskCanceledException)
{
break;
}
}
}
The problem is, it runs collecting for a bit. Then it comes into Aggregate() and it stops doing anything for the Task.Delay() then it Sends(). Then it does nothing again.
By does nothing, I mean Collect() stops executing.
I assume at some point it is blocking.
Is their a pattern here I'm missing. I want to run two tasks indefinitely and allow one of them to pause for a specified amount of time without affecting the other.
There is a couple of things that could be fixed.
As it said by Ben Voigt, "WaitAll" is really a blocker, you'd better to make a sequence of tasks with WhenAll
It can be worth to use Task.Factory.StartNew instead of instatiating new TaskFactory instance
StartNew may be not a best choice at all, see details here https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html