IsCancellationRequested is always false if CancellationTokenSource with delay - c#

I try to stop process by CancellationToken with timer.
But IsCancellationRequested is always false.
I tried to call cancellationToken.ThrowIfCancellationRequested(); it doesn't work.
public async Task<IReadOnlyList<ISearchableDevice>> SearchAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(100, cancellationToken);
cancellationToken.ThrowIfCancellationRequested(); // doesn't work
}
IReadOnlyList<ISearchableDevice> devices = new List<ISearchableDevice>();
return devices;
}
private void OnStartSearchCommandExecuted(object? p)
{
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
{
try
{
InProgress = true;
DeviceSearcher.SearchAsync(cts.Token)
.ContinueWith(task =>
{
InProgress = false;
}, cts.Token);
}
catch (Exception)
{
// TODO: Add exception handling
}
}
}
Where is the mistake?

The problem is that cts is disposed before timeout is reached. More precisely, it is disposed immediately after SearchAsync reaches first await.
You have at least two ways to solve this
Use await DeviceSearcher.SearchAsync instead of DeviceSearcher.SearchAsync().ContinueWith(). This is the best way, but it will force you to make the OnStartSearchCommandExecuted async.
Remove using block and call Dispose manually inside .ContinueWith (and move other post-searh logic inside ContinueWith and also do not forget call Dispose in case of exceptions (whether exception will occur in synchronous part of SearchAsync (before first await) or in asynchronous part. )).

Related

Is there a way I can cause a running method to stop immediately with a cts.Cancel();

I have code that creates a CancellationTokenSource and that passes it to a method.
I have code in another are of the app that issues a cts.Cancel();
Is there a way that I can cause that method to stop immediately without me having to wait for the two lines inside the while loop to finish?
Note that I would be okay if it caused an exception that I could handle.
public async Task OnAppearing()
{
cts = new CancellationTokenSource();
await GetCards(cts.Token);
}
public async Task GetCards(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
App.viewablePhrases = App.DB.GetViewablePhrases(Settings.Mode, Settings.Pts);
await CheckAvailability();
}
}
What I can suggest:
Modify GetViewablePhrases and CheckAvailability so you could pass the CancellationToken to them;
Use ct.ThrowIfCancellationRequested() inside these functions;
Try / Catch the OperationCanceledException inside GetCards;
As for your your functions I don't know how exactly they work inside. But let's assume you have a long running iteration inside one of them:
CheckAvailability(CancellationToken ct)
{
for(;;)
{
// if cts.Cancel() was executed - this method throws the OperationCanceledException
// if it wasn't the method does nothing
ct.ThrowIfCancellationRequested();
...calculations...
}
}
Or let's say you are going to access your database inside one of the function and you know that this process is going to take a while:
CheckAvailability(CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
AccessingDatabase();
}
This will not only prevent your functions from proceeding with execution, this also will set the executioner Task status as TaskStatus.Canceled
And don't forget to catch the Exception:
public async Task GetCards(CancellationToken ct)
{
try
{
App.viewablePhrases = App.DB.GetViewablePhrases(Settings.Mode, Settings.Pts, ct);
await CheckAvailability(ct);
}
catch(OperationCanceledException ex)
{
// handle the cancelation...
}
catch
{
// handle the unexpected exception
}
}
If you are OK with cancelling not the task, but the awaiting of the task, you could use a cancelable wrapper. In case of cancellation the underlying task will continue running, but the wrapper will complete immediately as canceled.
public static Task AsCancelable(this Task task,
CancellationToken cancellationToken)
{
var cancelable = new Task(() => { }, cancellationToken);
return Task.WhenAny(task, cancelable).Unwrap();
}
public static Task<T> AsCancelable<T>(this Task<T> task,
CancellationToken cancellationToken)
{
var cancelable = new Task<T>(() => default, cancellationToken);
return Task.WhenAny(task, cancelable).Unwrap();
}
Usage example:
await GetCards(cts.Token).AsCancelable(cts.Token);
This extension method can also be implemented using a TaskCompletionSource<T> (instead of the Task<T> constructor).

Canceling long running task on condition

I'm running cpu-bound task in asp.net mvc application. Some users can "subscribe" to this task and they should be notified of completion. But when task have no subscribers it must be cancelled. The task start via ajax request and cancel when .abort() method is called. In controller I have CancellationToken as parameter which determines cancellation.
The problem is that when one of subscribers calls abort (unsubscribe) the linked token cancels task despite other users are waiting for the result. How can I cancel CancellationToken after checking some condition? I can't check IsCancellationRequested prop after every loop iteration because I'm wrapping non-async method.
Users are notified with SignalR after task completion. I've tried to implement ConcurrentDictionary to check before cancelling whether task has subscribers or not.
private async Task<Diff> CompareAsync(Model modelName, CancellationToken ct)
{
try
{
return await Task.Factory.StartNew(() =>
{
ct.ThrowIfCancellationRequested();
return _someServiceName.CompareLines(modelName.LinesA, modelName.LinesB, ct);
}, ct, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
//do some things
}
}
I need something like this, but can't come up with any (not ugly)ideas:
private async Task<Diff> CompareAsync(Model modelName, CancellationToken ct)
{
try
{
return await Task.Factory.StartNew(() =>
{
using (var source = new CancellationTokenSource())
{
if (ct.IsCancellationRequested && CompareService.SharedComparison.TryGetValue(modelName.Hash, out var usersCount) && usersCount < 2)
{
source.Cancel();
}
return _someServiceName.CompareLines(modelName.LinesA, modelName.LinesB, source.Token);
}
}, ct, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
//do some things
}
}
You will have to maintain a thread safe subscriber count (most likely using lock), and only cancel the token when it's 0.
private int _subscribers;
private object _sync = new object();
private AddSubscribers()
{
Lock(_sync )
{
// do what ever you need to do
_subscribers++;
}
}
private RemoveSubscribers()
{
Lock(_sync )
{
// do what ever you need to do
_subscribers--;
if(_subscribers <= 0)
{
// cancel token
}
}
}
Note : Obviously this is not a complete solution and leaves a lot to the imagination.

How Do I Create a Looping Service inside an C# Async/Await application?

I have written a class with a method that runs as a long-running Task in the thread pool. The method is a monitoring service to periodically make a REST request to check on the status of another system. It's just a while() loop with a try()catch() inside so that it can handle its own exceptions and and gracefully continuing if something unexpected happens.
Here's an example:
public void LaunchMonitorThread()
{
Task.Run(() =>
{
while (true)
{
try
{
//Check system status
Thread.Sleep(5000);
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
});
}
It works fine, but I want to know if there's another pattern I could use that would allow the Monitor method to run as regular part of a standard Async/Await application, instead of launching it with Task.Run() -- basically I'm trying to avoid fire-and-forget pattern.
So I tried refactoring the code to this:
public async Task LaunchMonitorThread()
{
while (true)
{
try
{
//Check system status
//Use task.delay instead of thread.sleep:
await Task.Delay(5000);
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
}
But when I try to call the method in another async method, I get the fun compiler warning:
"Because this call is not awaited, execution of the current method continues before the call is completed."
Now I think this is correct and what I want. But I have doubts because I'm new to async/await. Is this code going to run the way I expect or is it going to DEADLOCK or do something else fatal?
What you are really looking for is the use of a Timer. Use the one in the System.Threading namespace. There is no need to use Task or any other variation thereof (for the code sample you have shown).
private System.Threading.Timer timer;
void StartTimer()
{
timer = new System.Threading.Timer(TimerExecution, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
}
void TimerExecution(object state)
{
try
{
//Check system status
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
From the documentation
Provides a mechanism for executing a method on a thread pool thread at specified intervals
You could also use System.Timers.Timer but you might not need it. For a comparison between the 2 Timers see also System.Timers.Timer vs System.Threading.Timer.
If you need fire-and-forget operation, it is fine. I'd suggest to improve it with CancellationToken
public async Task LaunchMonitorThread(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
try
{
//Check system status
//Use task.delay instead of thread.sleep:
await Task.Delay(5000, token);
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
}
besides that, you can use it like
var cancellationToken = new CancellationToken();
var monitorTask = LaunchMonitorThread(cancellationToken);
and save task and/or cancellationToken to interrupt monitor wherever you want
The method Task.Run that you use to fire is perfect to start long-running async functions from a non-async method.
You are right: the forget part is not correct. If for instance your process is going to close, it would be neater if you kindly asked the started thread to finish its task.
The proper way to do this would be to use a CancellationTokenSource. If you order the CancellationTokenSource to Cancel, then all procedures that were started using Tokens from this CancellationTokenSource will stop neatly within reasonable time.
So let's create a class LongRunningTask, that will create a long running Task upon construction and Cancel this task using the CancellationTokenSource upon Dispose().
As both the CancellationTokenSource as the Task implement IDisposable the neat way would be to Dispose these two when the LongRunningTask object is disposed
class LongRunningTask : IDisposable
{
public LongRunningTask(Action<CancellationToken> action)
{ // Starts a Task that will perform the action
this.cancellationTokenSource = new CancellationTokenSource();
this.longRunningTask = Task.Run( () => action (this.cancellationTokenSource.Token));
}
private readonly CancellationTokenSource cancellationTokenSource;
private readonly Task longRunningTask;
private bool isDisposed = false;
public async Task CancelAsync()
{ // cancel the task and wait until the task is completed:
if (this.isDisposed) throw new ObjectDisposedException();
this.cancellationTokenSource.Cancel();
await this.longRunningTask;
}
// for completeness a non-async version:
public void Cancel()
{ // cancel the task and wait until the task is completed:
if (this.isDisposed) throw new ObjectDisposedException();
this.cancellationTokenSource.Cancel();
this.longRunningTask.Wait;
}
}
Add a standard Dispose Pattern
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (disposing && !this.isDisposed)
{ // cancel the task, and wait until task completed:
this.Cancel();
this.IsDisposed = true;
}
}
Usage:
var longRunningTask = new LongRunningTask( (token) => MyFunction(token)
...
// when application closes:
await longRunningTask.CancelAsync(); // not necessary but the neat way to do
longRunningTask.Dispose();
The Action {...} has a CancellationToken as input parameter, your function should regularly check it
async Task MyFunction(CancellationToken token)
{
while (!token.IsCancellationrequested)
{
// do what you have to do, make sure to regularly (every second?) check the token
// when calling other tasks: pass the token
await Task.Delay(TimeSpan.FromSeconds(5), token);
}
}
Instead of checking for Token, you could call token.ThrowIfCancellationRequested. This will throw an exception that you'll have to catch

C# How to handle cancel task with eventhandler inside

I have requirement to update ui control when status of dependent service will change. I have this sample code, which polling service api to get status and sends result to recalculate and update ui by main thread:
public void StartObserving() {
this.cts = new CancellationTokenSource();
this.cts.Token.ThrowIfCancellationRequested();
this.isRunning = true;
var token = this.cts.Token;
Task.Run(async () =>
{
try
{
while (this.isRunning)
{
var result = this.serviceAPI.GetStatus();
this.OnServiceStatusChanged(result);
await Task.Delay(3000);
}
}
catch (OperationCanceledException)
{
this.isRunning = false;
}
catch (Exception ex)
{
this.isRunning = false;
this.logger.LogError(ex);
}
}, token);
}
And the problem is when I want to cancel above Task. When I call this.cts.Cancel() in another method in this class, I get Exception 'A task was canceled' on dispatcher which was triggered by EventHandler: OnServiceStatusChanged
How I should properly implement this scenario?
I would simply check whether the token in cancelled in the inner loop, and exit the loop if it is. No need to pass the token to the Task.Run() method.
public void StartObserving()
{
this.cts = new CancellationTokenSource();
var token = this.cts.Token;
Task.Run(async () =>
{
try
{
while (!token.IsCancellationRequested)
{
var result = this.serviceAPI.GetStatus();
this.OnServiceStatusChanged(result);
await Task.Delay(3000);
}
}
catch
{
}
});
}
Tried to simulate this behavior in a console app. Task started, but after calling cts.Cancel(), the task continues to execute... Very strange.
However, I could cancel the task by simply setting this.isRunning to false (instead of calling cts.Cancel()). But I am not sure if this is the solution you want.
If serviceAPI.GetStatus() is a blocking call that waits indeffinitly, then you cannot properly cancel this task.
Proper cancellation of async methods involves marking safe cancellation points with CancellationToken.ThrowIfCancellationRequested().
You would have to rewrite serviceAPI.GetStatus() as an async method that you await the result of. It should contain calls to CancellationToken.ThrowIfCancellationRequested() at points where it can be safely cancelled. You would want to pass the cancellation token in to both that method, and the call to Task.Delay() for optimal performance.

Disposing CancellationTokenRegistrations

Contrived example, but suppose I have the following in an async method:
var cts = new CancellationTokenSource();
cts.CancelAfter(2000);
cts.Token.Register(Callback);
SomethingThatMightThrow();
await Task.Delay(10000, cts.Token);
This works as expected insofar as after a couple of seconds Callback is called. However, I want to dispose the registration after the Task.Delay, so suppose I make the following modification:
var cts = new CancellationTokenSource();
cts.CancelAfter(2000);
using (cts.Token.Register(Callback))
{
SomethingThatMightThrow();
await Task.Delay(10000, cts.Token);
}
In this case, Callback is not called, presumably because the exception from the await Task.Delay... causes the registration to be disposed before it gets invoked.
What's the best way of ensuring both that Callback gets called on cancellation and that the registration always gets disposed?
I did think of the following, but I'm not sure how robust it is:
var cts = new CancellationTokenSource();
cts.CancelAfter(2000);
var ctr = cts.Token.Register(Callback);
try
{
SomethingThatMightThrow();
await Task.Delay(10000, cts.Token);
}
finally
{
if (!cts.Token.IsCancellationRequested)
ctr.Dispose();
}
CancellationToken.Register is generally used to interop the new CancellationToken system with an older system that uses some other kind of notification for cancellation. It is not intended for use as a general-purpose "cancellation callback".
If you want to respond some way when an operation is canceled, then you just catch the appropriate exception:
using (var cts = new CancellationTokenSource())
{
cts.CancelAfter(2000);
SomethingThatMightThrow();
try
{
await Task.Delay(10000, cts.Token);
}
catch (OperationCanceledException)
{
Callback();
}
}
it's not so much that the method might not throw the exception, as
that it might not throw it as quickly as I'd like. I've noticed some
of the Azure SDK async methods, for example, can take quite a while to
respond to cancellation being signalled on the token
As per you comment, you can choose to create your own timer to explicitly specify when a method "is taking too long to run" according to your standards. For that you can use Task.WhenAny:
using (var cts = new CancellationTokenSource())
{
try
{
var cancellationDelayTask = Task.Delay(2000, cts.Token);
var taskThatMightThrow = SomethingThatMightThrowAsync(cts.Token);
if ((await Task.WhenAny(taskThatMightThrow, cancellationDelayTask))
== cancellationDelayTask)
{
// Task.Delay "timeout" finished first.
}
}
catch (OperationCanceledException)
{
Callback();
}
}

Categories