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

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.

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 implement hosted service for a singleton job that can be started and stopped?

I want to create a hosted service that can be started and stopped through user interaction. The service should not be started automatically, attempting to start it when it is already started should have no effect, and it should be possible to check whether the service is currently running. The service will eventually finish running, at which point it should transition to the stopped state until it is started again.
Below is my attempt at implementing this and it seems to be working, but I was wondering if there are any glaring flaws with this approach, or if there is a simpler solution.
There are two parts, a singleton FooJobControl which can be injected into controllers to start or stop the service, and the hosted service FooJob which waits for a signal from FooJobControl to start working and signals when it has stopped.
in Startup.cs
services.AddHostedService<FooJob>();
services.AddSingleton<FooJobControl>();
FooJobControl.cs
public class FooJobControl
{
private AsyncAutoResetEvent _evt = new AsyncAutoResetEvent(false);
private CancellationTokenSource _cts;
private object _sync = new object();
public void Start()
{
lock (_sync)
{
if (_cts == null)
{
_evt.Set();
}
}
}
public void Stop()
{
lock (_sync)
{
if (_cts != null)
{
_cts.Cancel();
}
}
}
public bool IsRunning
{
get
{
lock (_sync)
{
return _cts != null && !_cts.IsCancellationRequested;
}
}
}
public async Task<CancellationToken> JobStart(CancellationToken stoppingToken)
{
await _evt.WaitAsync(stoppingToken);
lock (_sync)
{
_cts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken);
return _cts.Token;
}
}
public void JobEnd()
{
lock (_sync)
{
_cts = null;
}
}
}
FooJob.cs
public class FooJob : BackgroundService
{
private FooJobControl _control;
private ILogger<FooJob> _logger;
public FooJob(ILogger<FooJob> logger, FooJobControl control)
{
_logger = logger;
_control = control;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (true)
{
CancellationToken token = await _control.JobStart(stoppingToken);
try
{
await DoWork(token);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to run job");
}
_control.JobEnd();
}
}
private async Task DoWork(CancellationToken token)
{
for (int i = 1; i <= 10; i++)
{
if (token.IsCancellationRequested)
{
_logger.LogInformation("DoWork Cancelled");
return;
}
_logger.LogInformation("DoWork " + i);
await Task.Delay(500);
}
}
}

DeadLock on task.Wait() with Task which edit UI

I'm trying to find some solutions to my problem here, but with no result (or I just do not get them right) so if anyone could help / explain i will be really gratefull.
I'm just developing a tool for system administrators using Win Form and now I need to create a continuous ping on the selected machine which is running on the background. There is an indicator for Online status on UI which I need to edit with background ping. So right now I'm in this state:
Class A (Win form):
ClassB activeRelation = new ClassB();
public void UpdateOnline(Relation pingedRelation)
{
//There is many Relations at one time, but form shows Info only for one...
if (activeRelation == pingedRelation)
{
if (p_Online.InvokeRequired)
{
p_Online.Invoke(new Action(() =>
p_Online.BackgroundImage = (pingedRelation.Online) ? Properties.Resources.Success : Properties.Resources.Failure
));
}
else
{
p_Online.BackgroundImage = (pingedRelation.Online) ? Properties.Resources.Success : Properties.Resources.Failure;
}
}
}
//Button for tunring On/Off the background ping for current machine
private void Btn_PingOnOff_Click(object sender, EventArgs e)
{
Button btn = (sender is Button) ? sender as Button : null;
if (btn != null)
{
if (activeRelation.PingRunning)
{
activeRelation.StopPing();
btn.Image = Properties.Resources.Switch_Off;
}
else
{
activeRelation.StartPing(UpdateOnline);
btn.Image = Properties.Resources.Switch_On;
}
}
}
Class B (class thats represent relation to some machine)
private ClassC pinger;
public void StartPing(Action<Relation> action)
{
pinger = new ClassC(this);
pinger.PingStatusUpdate += action;
pinger.Start();
}
public void StopPing()
{
if (pinger != null)
{
pinger.Stop();
pinger = null;
}
}
Class C (background ping class)
private bool running = false;
private ClassB classb;
private Task ping;
private CancellationTokenSource tokenSource;
public event Action<ClassB> PingStatusUpdate;
public ClassC(ClassB classB)
{
this.classB = classB;
}
public void Start()
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
ping = PingAction(token);
running = true;
}
public void Stop()
{
if (running)
{
tokenSource.Cancel();
ping.Wait(); //And there is a problem -> DeadLock
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
private async Task PingAction(CancellationToken ct)
{
bool previousResult = RemoteTasks.Ping(classB.Name);
PingStatusUpdate?.Invoke(classB);
while (!ct.IsCancellationRequested)
{
await Task.Delay(pingInterval);
bool newResult = RemoteTasks.Ping(classB.Name);
if (newResult != previousResult)
{
previousResult = newResult;
PingStatusUpdate?.Invoke(classB);
}
}
}
So the problem is in deadlock when I cancel token and Wait() for task to complete -> it's still running, but While(...) in task is finished right.
You have a deadlock because ping.Wait(); blocks UI thread.
You should wait for task asynchronously using await.
So, if Stop() is event handler then change it to:
public async void Stop() // async added here
{
if (running)
{
tokenSource.Cancel();
await ping; // await here
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
If it is not:
public async Task Stop() // async added here, void changed to Task
{
if (running)
{
tokenSource.Cancel();
await ping; // await here
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
As mentioned by #JohnB async methods should have Async suffix so, the method should be named as StopAsync().
Similar problem and solution are explained here - Do Not Block On Async Code
You should avoid synchronous waiting on tasks, so you should always use await with tasks instead of Wait() or Result. Also, as pointed by #Fildor you should use async-await all the way to avoid such situations.

Task<T> queueing

I have a following method
public async Task<T> SomeMethod(parameters)
{
// here we execute some instructions which are not thread safe
}
I need SomeMethod to return a Task, so that other methods can run (await) it asynchronously, and not block the UI thread.
The problem is that SomeMethod can be called in parallel, since the execution is returned to UI thread, and that will raise exception, since some of the calls inside SomeMethod() are not thread safe.
What is the best way to ensure that all calls to SomeMethod are queued (and awaitable), and that this queue will be executed in sequence?
Use AsyncLock to prevent two threads from executing a single block of code :
(A traditional lock will not work, because you can't use an await keyword inside of it)
private AsyncLock myAsyncLock = new AsyncLock();
public async Task<T> SomeMethod(parameters)
{
using (await myAsyncLock.LockAsync())
{
// here we execute some instructions which are not thread safe
}
}
public class AsyncLock
{
private readonly AsyncSemaphore m_semaphore;
private readonly Task<Releaser> m_releaser;
public AsyncLock()
{
m_semaphore = new AsyncSemaphore(1);
m_releaser = Task.FromResult(new Releaser(this));
}
public Task<Releaser> LockAsync()
{
var wait = m_semaphore.WaitAsync();
return wait.IsCompleted ?
m_releaser :
wait.ContinueWith((_, state) => new Releaser((AsyncLock)state),
this, System.Threading.CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
public struct Releaser : IDisposable
{
private readonly AsyncLock m_toRelease;
internal Releaser(AsyncLock toRelease) { m_toRelease = toRelease; }
public void Dispose()
{
if (m_toRelease != null)
m_toRelease.m_semaphore.Release();
}
}
}
// http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266983.aspx
public class AsyncSemaphore
{
private readonly static Task s_completed = Task.FromResult(true);
private readonly Queue<TaskCompletionSource<bool>> m_waiters = new Queue<TaskCompletionSource<bool>>();
private int m_currentCount;
public AsyncSemaphore(int initialCount)
{
if (initialCount < 0) throw new ArgumentOutOfRangeException("initialCount");
m_currentCount = initialCount;
}
public Task WaitAsync()
{
lock (m_waiters)
{
if (m_currentCount > 0)
{
--m_currentCount;
return s_completed;
}
else
{
var waiter = new TaskCompletionSource<bool>();
m_waiters.Enqueue(waiter);
return waiter.Task;
}
}
}
public void Release()
{
TaskCompletionSource<bool> toRelease = null;
lock (m_waiters)
{
if (m_waiters.Count > 0)
toRelease = m_waiters.Dequeue();
else
++m_currentCount;
}
if (toRelease != null)
toRelease.SetResult(true);
}
}

Thread synchronization (locking) that only releases to the last-in thread

What is the proper way to ensure that only the 'last-in' thread is given access to a mutex/locked region while intermediary threads do not acquire the lock?
Example sequence:
A acquires lock
B waits
C waits
B fails to acquire lock*
A releases lock
C acquires lock
*B should fail to acquire the lock either via an exception (as in SemaphoreSlim.Wait(CancellationToken) or a boolean Monitor.TryEnter() type construct.
I can think of several similar schemes to achieve this (such as using a CancellationTokenSource and SemaphoreSlim), but none of them seem particularly elegant.
Is there a common practice for this scenario?
This should work like you want, it uses a SemaphoreSlim with a size of 1 to control it. I also added support for passing in a CancelationToken to cancel waiting for the lock early, it also supports WaitAsync returning a task instead of blocking.
public sealed class LastInLocker : IDisposable
{
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);
private CancellationTokenSource _cts = new CancellationTokenSource();
private bool _disposed = false;
public void Wait()
{
Wait(CancellationToken.None);
}
public void Wait(CancellationToken earlyCancellationToken)
{
if(_disposed)
throw new ObjectDisposedException("LastInLocker");
var token = ReplaceTokenSource(earlyCancellationToken);
_semaphore.Wait(token);
}
public Task WaitAsync()
{
return WaitAsync(CancellationToken.None);
}
public async Task WaitAsync(CancellationToken earlyCancellationToken)
{
if (_disposed)
throw new ObjectDisposedException("LastInLocker");
var token = ReplaceTokenSource(earlyCancellationToken);
//I await here because if ReplaceTokenSource thows a exception I want the
//observing of that exception to be deferred until the caller awaits my
//returned task.
await _semaphore.WaitAsync(token).ConfigureAwait(false);
}
public void Release()
{
if (_disposed)
throw new ObjectDisposedException("LastInLocker");
_semaphore.Release();
}
private CancellationToken ReplaceTokenSource(CancellationToken earlyCancellationToken)
{
var newSource = CancellationTokenSource.CreateLinkedTokenSource(earlyCancellationToken);
var oldSource = Interlocked.Exchange(ref _cts, newSource);
oldSource.Cancel();
oldSource.Dispose();
return newSource.Token;
}
public void Dispose()
{
_disposed = true;
_semaphore.Dispose();
_cts.Dispose();
}
}
Here is a little test program that re-creates your test example
internal class Program
{
static LastInLocker locker = new LastInLocker();
private static void Main(string[] args)
{
Task.Run(() => Test("A"));
Thread.Sleep(500);
Task.Run(() => Test("B"));
Thread.Sleep(500);
Task.Run(() => Test("C"));
Console.ReadLine();
}
private static void Test(string name)
{
Console.WriteLine("{0} waits for lock", name);
try
{
locker.Wait();
Console.WriteLine("{0} acquires lock", name);
Thread.Sleep(4000);
locker.Release();
Console.WriteLine("{0} releases lock", name);
}
catch (Exception)
{
Console.WriteLine("{0} fails to acquire lock", name);
}
}
}
outputs
A waits for lock
A acquires lock
B waits for lock
C waits for lock
B fails to acquire lock
A releases lock
C acquires lock
C releases lock
Try this:
public interface ILocker
{
bool GetLock();
void Release();
}
class Locker : ILocker
{
private long m_NumberOfTimeGetLockWasCalled = 0;
private readonly object m_LockingObject = new object();
private readonly object m_LockingObject2 = new object();
public bool GetLock()
{
long lock_count = 0;
var lock_was_taken = false;
lock(m_LockingObject)
{
lock_count = m_NumberOfTimeGetLockWasCalled++;
lock_was_taken = Monitor.TryEnter(m_LockingObject2);
if (lock_was_taken)
return true;
}
while(!lock_was_taken)
{
Thread.Sleep(5);
lock(m_LockingObject)
{
if (lock_count != m_NumberOfTimeGetLockWasCalled)
return false;
lock_was_taken = Monitor.TryEnter(m_LockingObject2);
if (lock_was_taken)
break;
}
}
return true;
}
public void Release()
{
Monitor.Exit(m_LockingObject2);
}
}

Categories

Resources