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

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();
}
}

Related

How to catch that Task.Delay() canceled and do a callback?

Lets say I have a worker class.
public sealed class Worker : IDisposable
{
private bool _isRunning;
private CancellationTokenSource _cts;
private readonly Action _action;
private readonly int _millisecondsDelay;
private readonly object _lock = new object();
public Worker(Action action, int millisecondsDelay)
{
_action = action;
_millisecondsDelay = millisecondsDelay = 5000;
}
public void Start()
{
lock (_lock)
{
if (!_isRunning)
{
_isRunning = true;
Run();
}
}
}
public void Cancel()
{
lock (_lock)
{
if (_isRunning) _cts.Cancel();
}
}
private void Run()
{
using (_cts) _cts = new CancellationTokenSource();
Task.Run(async () => { await DoAsync(_cts.Token); });
}
private async Task DoAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
//Log.Message1("____REFRESHING STATUS____");
_action();
await Task.Delay(_millisecondsDelay, cancellationToken);
}
//this code is unreachable
lock (_lock)
{
_isRunning = false;
}
}
public void Dispose()
{
try
{
_cts?.Cancel();
}
finally
{
if (_cts != null)
{
_cts.Dispose();
_cts = null;
}
}
}
}
The problem is the code _isRunning = false; is unreachable. I mean more likely when a caller call Cancel method the worker will be awaiting Task.Delay. So how I can call smth(here it's _isRunning = false;) after my Task will be canceled ? In other words I need to be sure that my worker is not running(it's not the cancelled state)
To answer your literal question, you can use a finally block:
private async Task DoAsync(CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
//Log.Message1("____REFRESHING STATUS____");
_action();
await Task.Delay(_millisecondsDelay, cancellationToken);
}
}
finally
{
lock (_lock)
{
_isRunning = false;
}
}
}
But I have some concerns about this "worker" approach:
I'm not a huge fan of the fire-and-forget inside Run. I suspect you'll want to change that.
Mixing lock with asynchronous code can be problematic. You should be absolutely sure that this is what you really want to do.
It may be worthwhile stepping back and reconsidering what you are actually wanting to do with this code.

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 add more milliseconds Delay in delay var c#

I would like to add more delay in delay var while the execution waits
Example:
private System.Threading.Tasks.Task delayVar; //Delay var
private async void createDelay() //First function
{
delayVar = System.Threading.Tasks.Task.Delay(milliseconds);
await delayVar;
}
private void addDelay() //Second function
{
delayVar.Milliseconds +=5000;
}
Thanks.
You can't "reset" a Task.Delay, but you can reset a timer which makes it an ideal candidate to solve this problem.
Here's an example:
private System.Threading.Timer timer;
public void Start()
{
timer = new System.Threading.Timer(_ => fireMyCode());
restartTimer();
}
private void onFileChanged(object sender, EventArgs e)
{
restartTimer();
}
private void restartTimer()
{
timer.Change(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
}
But you don't have to use timers, you can still use Task.Delay with an additional task: the idea is to wait on two tasks, the delay and waiting for the files to change (you can use TaskCompletionSource to "create" a task from an event). If the delay task completes first, fire your code.
Here's an example:
TaskCompletionSource<object> fileChanged = new TaskCompletionSource<object>();
private void onFileChanged(object sender, EventArgs e)
{
fileChanged.TrySetResult(null);
}
private async Task endlessLoop()
{
while (true)
{
await handleFilesNotChanged();
}
}
private async Task handleFilesNotChanged()
{
Task timeout = Task.Delay(TimeSpan.FromMinutes(5));
Task waitForFile = fileChanged.Task;
if (await Task.WhenAny(timeout, waitForFile) == timeout)
{
fireMyCode();
}
fileChanged = new TaskCompletionSource<object>();
}

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);

Canceling Task Delay in .Net 4.0

I am currently trying to implement a substitute for .Net 4.5's Task.Delay() method in a program that must target .Net 4.0. I found the following code at this blog.
/* You can write Task-based asynchronous methods by utilizing a TaskCompletionSource.
A TaskCompletionSource gives you a 'slave' Task that you can manually signal.
Calling SetResult() signals the task as complete, and any continuations kick off. */
void Main()
{
for (int i = 0; i < 10000; i++)
{
Task task = Delay (2000);
task.ContinueWith (_ => "Done".Dump());
}
}
Task Delay (int milliseconds) // Asynchronous NON-BLOCKING method
{
var tcs = new TaskCompletionSource<object>();
new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1);
return tcs.Task;
}
Tasks are fairly new to me. System.Threading.Timer and TaskCompletionSource are brand new to me (as of today), and I'm struggling a bit with them. All that aside, I'm wondering how I might add CancellationToken functionality to this code. I'm assuming I could add a parameter to the Delay() method like this:
Task Delay (int milliseconds, CancellationToken token) // Asynchronous NON-BLOCKING method
{
var tcs = new TaskCompletionSource<object>();
new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1);
return tcs.Task;
}
... but then, where do I put the logic for checking the token and getting out of the method? Somewhere in the callback? Is this even possible?
I've tried to change your code as little as possible but here is a working example that behaves in the same way as Task.Delay.
It's important to note that I use TrySetCanceled and TrySetResult because the Timer could finish after the task is canceled. Ideally you want to stop the timer.
Also note a canceled task will throw a TaskCanceledException
static void Main(string[] args)
{
// A cancellation source that will cancel itself after 1 second
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1));
try
{
// This will only wait 1 second because as it will be cancelled.
Task t = Delay(5000, cancellationTokenSource.Token);
t.Wait();
Console.WriteLine("The task completed");
}
catch (AggregateException exception)
{
// Expecting a TaskCanceledException
foreach (Exception ex in exception.InnerExceptions)
Console.WriteLine("Exception: {0}", ex.Message);
}
Console.WriteLine("Done");
Console.ReadLine();
}
private static Task Delay(int milliseconds, CancellationToken token)
{
var tcs = new TaskCompletionSource<object>();
token.Register(() => tcs.TrySetCanceled());
Timer timer = new Timer(_ => tcs.TrySetResult(null));
timer.Change(milliseconds, -1);
return tcs.Task;
}
Reading a bit more into your question. If you need Task.Delay and you're targeting .NET 4.0 then you should use the Microsoft Async nuget package from http://www.nuget.org/packages/Microsoft.Bcl.Async/ it contains the method TaskEx.Delay
Like this:
token.Register(() => tcs.TrySetCancelled());
Here you are a version that prevents disposal of timer by the garbage collector
public static Task Delay(int milliseconds, CancellationToken token)
{
var tcs = new TaskCompletionSource<object>();
var timer = new OneShotTimer((t) => {
using ((OneShotTimer)t)
tcs.SetResult(null);
});
token.Register(() => {
if (timer.TryCancel())
{
using (timer)
tcs.SetCanceled();
}
});
timer.Start(milliseconds);
return tcs.Task;
}
public class OneShotTimer : IDisposable
{
private readonly object sync = new object();
private readonly TimerCallback oneShotCallback;
private readonly Timer timer;
private bool isActive;
public OneShotTimer(TimerCallback oneShotCallback, int dueTime = Timeout.Infinite)
{
this.oneShotCallback = oneShotCallback;
this.isActive = dueTime != Timeout.Infinite;
this.timer = new Timer(callback, this, dueTime, Timeout.Infinite);
}
public void Dispose()
{
timer.Dispose();
}
public void Start(int dueTime)
{
if (!tryChange(true, dueTime))
throw new InvalidOperationException("The timer has already been started");
}
public bool TryCancel()
{
return tryChange(false, Timeout.Infinite);
}
public bool tryChange(bool targetIsActive, int dueTime)
{
bool result = false;
lock (sync)
{
if (isActive != targetIsActive)
{
result = true;
isActive = targetIsActive;
timer.Change(dueTime, Timeout.Infinite);
}
}
return result;
}
private static void callback(object state)
{
var oneShotTimer = (OneShotTimer)state;
if (oneShotTimer.TryCancel())
oneShotTimer.oneShotCallback(oneShotTimer);
}
}

Categories

Resources