I am using the oneDrive API aka Live SDK. using c# and XAML you can issue http GET request using the above mentioned method like so:
case A:
public async void methodA()
{
try
{
var meResult = await connectClient.GetAsync("me");
}
catch(Exception ex)
{
//exception handling
}
doSomething();
}
Case B:
public async void methodB()
{
try
{
var meResult = await connectClient.GetAsync("me", token);
}
catch(Exception ex)
{
//exception handling
}
doSomething();
}
where token is cancellationToken; that will wait for specific time and then cancel the request.
if there is no internet connection:
In case A the methodA() will hang on the getAsync and never progress so the doSomething() method is never called.
In case B the methodB() will handle the exception when the cancellationToken cancel the call then progress to the doSomething() method.
Now my concern and my question is:
I am afraid that keeping the await call hanging there will mean locking one thread and will affect the performance especially if the user clicked the button many times to call the methodA().
Is my concern justified?
is there a way for the .NET runtime to know that this awaited operation is timed out?? and eventually cancel it?
If you want to pass in a CancellationToken that times out after a certain amount of time, there is a constructor for CancellationTokenSource that does just that:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var meResult = await connectClient.GetAsync("me", cts.Token);
By the way, even if you don't do this, a .NET thread won't be locked while you wait for GetAsync to finish - that's one of the direct benefits of using await instead of .Result.
Related article by Stephen Toub: Coalescing CancellationTokens from Timeouts
Related
So I had a problem with a third party library where the call could get stuck and never return even when calling cancellationToken.Cancel. The below is a prototype that take care of this situation and that it works.
public async Task MainAsync()
{
try
{
await StartAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine("Exception thrown");
}
}
private async Task<string> StartAsync()
{
var cts = new CancellationTokenSource();
cts.CancelAfter(3 * 1000);
var tcs = new TaskCompletionSource<string>();
cts.Token.Register(() => { Console.WriteLine("Cancelled"); tcs.TrySetCanceled(); });
return await (await Task.WhenAny(tcs.Task, LongComputationAsync())
.ConfigureAwait(false)).ConfigureAwait(false);
}
private async Task<string> LongComputationAsync()
{
await Task.Delay(1 * 60 * 1000).ConfigureAwait(false);
return "Done";
}
So the Above will wait 3 seconds, and it will throw a TaskCancelledException like it should.
If you then change the method LongComputationAsync to the following:
private Task<string> LongComputationAsync()
{
Task.Delay(1 * 60 * 1000).ConfigureAwait(false).GetAwaiter().GetResult();
return Task.FromResult("Done");
}
I would still expect this to have the same behaviour, but what this does is that, it will wait the full 1 minute (specified in the LongComputationAsync()) then throw the TaskCancelledException.
Can anyone explain this to me? On how this is working, or if this is the correct behaviour to begin with.
Can anyone explain this to me?
Sure. The problem doesn't have anything to do with WhenAny. Rather, the problem is that the code assumes a method is asynchronous when it's synchronous.
This is a relatively easy mistake to make. But as a general rule, a method with an asynchronous signature may be asynchronous; it does not have to be asynchronous.
As I describe on my blog, asynchronous methods begin executing synchronously, just like synchronous methods. It is only when they hit an await that they may run asynchronously (and even then, they may continue synchronously).
So, the new version of LongCompuationAsync is synchronous, and it executes the entire method before returning the task to StartAsync, which then passes it to WhenAny.
I find myself writing code like this a lot:
try
{
cancellationTokenSource.Cancel();
await task.ConfigureAwait(false); // this is the task that was cancelled
}
catch(OperationCanceledException)
{
// Cancellation expected and requested
}
Given that I requested the cancellation, it is expected and I'd really like the exception to be ignored. This seems like a common case.
Is there a more concise way to do this? Have I missed something about cancellation? It seems like there should be a task.CancellationExpected() method or something.
There is a built-in mechanism, the Task.WhenAny method used with a single argument, but it's not very intuitive.
Creates a task that will complete when any of the supplied tasks have completed.
await Task.WhenAny(task); // await the task ignoring exceptions
if (task.IsCanceled) return; // the task is completed at this point
var result = await task; // can throw if the task IsFaulted
It is not intuitive because the Task.WhenAny is normally used with at least two arguments. Also it is slightly inefficient because the method accepts a params Task<TResult>[] tasks argument, so on every invocation an array is allocated in the heap.
I don't think there is anything built-in, but you could capture your logic in extension methods (one for Task, one for Task<T>):
public static async Task IgnoreWhenCancelled(this Task task)
{
try
{
await task.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
}
}
public static async Task<T> IgnoreWhenCancelled<T>(this Task<T> task)
{
try
{
return await task.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
return default;
}
}
Then you can write your code simpler:
await task.IgnoreWhenCancelled();
or
var result = await task.IgnoreWhenCancelled();
(You might still want to add .ConfigureAwait(false) depending on your synchronization needs.)
I assume whatever task is doing uses CancellationToken.ThrowIfCancellationRequested() to check for cancellation. That throws an exception by design.
So your options are limited. If task is an operation you wrote, you could make it not use ThrowIfCancellationRequested() and instead check IsCancellationRequested and end gracefully when needed. But as you know, the task's status won't be Canceled if you do that.
If it uses code you didn't write, then you don't have a choice. You'll have to catch the exception. You can use extension methods to avoid repeating code (Matt's answer), if you want. But you'll have to catch it somewhere.
The cancellation pattern available in C# in called cooperative cancellation.
This basically means that, in order to cancel any operation, there should be two actors which need to collaborate. One of them is the actor requesting the cancellation and the other is the actor listening to cancellation requests.
In order to implement this pattern you need an instance of CancellationTokenSource, which is an object that you can use in order to get an instance of CancellationToken. The cancellation is requested on the CancellationTokenSource instance and is propagated to the CancellationToken.
The following piece of code shows you this pattern in action and hopefully clarifies your doubt about cancellation:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp2
{
public static class Program
{
public static async Task Main(string[] args)
{
using (var cts = new CancellationTokenSource())
{
CancellationToken token = cts.Token;
// start the asyncronous operation
Task<string> getMessageTask = GetSecretMessage(token);
// request the cancellation of the operation
cts.Cancel();
try
{
string message = await getMessageTask.ConfigureAwait(false);
Console.WriteLine($"Operation completed successfully before cancellation took effect. The message is: {message}");
}
catch (OperationCanceledException)
{
Console.WriteLine("The operation has been canceled");
}
catch (Exception)
{
Console.WriteLine("The operation completed with an error before cancellation took effect");
throw;
}
}
}
static async Task<string> GetSecretMessage(CancellationToken cancellationToken)
{
// simulates asyncronous work. notice that this code is listening for cancellation
// requests
await Task.Delay(500, cancellationToken).ConfigureAwait(false);
return "I'm lost in the universe";
}
}
}
Pay attention to the comment and notice that all the 3 outputs for the program are possible.
There is no way to predict which of them will be the actual program result.
The point is that when you await for the task completion you don't know what actually is going to happen. The operation may succeeds or fails before the cancellation took effect, or maybe the cancellation request can be observed by the operation before it runs to completion or fails for an error. From the calling code point of view, all these outcomes are possible and you have no way to make a guess. You need to handle all cases.
So, basically, your code is correct and you are handling the cancellation the way you should.
This book is an excellent reference to learn these things.
My final solution was to create an extension method as suggested by Matt Johnson-Pint. However, I return a boolean indicating whether the task was canceled as shown in Vasil Oreshenski's answer.
public static async Task<bool> CompletionIsCanceledAsync(this Task task)
{
if (task.IsCanceled) return true;
try
{
await task.ConfigureAwait(false);
return false;
}
catch (OperationCanceledException)
{
return true;
}
}
This method has been fully unit tested. I picked the name to be similar to the WaitForCompletionStatus() method in the ParallelExtensionsExtras sample code and the IsCanceled property.
If you are expecting the task to be cancelled BEFORE the await you should check the state of the cancellation token source.
if (cancellationTokenSource.IsCancellationRequested == false)
{
await task;
}
EDIT: As mentioned in the comments this won't do any good if the task is cancelled while awaited.
EDIT 2: This approach is overkill because it acquires additional resource - in hot path this may have performance hit. (i am using SemaphoreSlim but you can use another sync. primitive with the same success)
This is an extension method over existing task. The extension method will return new task which holds information if the original task was cancelled.
public static async Task<bool> CancellationExpectedAsync(this Task task)
{
using (var ss = new SemaphoreSlim(0, 1))
{
var syncTask = ss.WaitAsync();
task.ContinueWith(_ => ss.Release());
await syncTask;
return task.IsCanceled;
}
}
Here is a simple usage:
var cancelled = await originalTask.CancellationExpectedAsync();
if (cancelled) {
// do something when cancelled
}
else {
// do something with the original task if need
// you can acccess originalTask.Result if you need
}
How it works:
Overall it waits for the original task to complete and returns information if was cancelled. The SemaphoraSlim is usually used to limit the access to some resource(expensive) but in this case i am using it to await until the original task has finished.
Notes:
It does not returns the original task. So if you need something that has been returned from it you should inspect the original task.
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
I have a blocking operation that reads from a queue, but it can take a timeout. I can easily convert this to an "async" operation:
public async Task<IMessage> ReceiveAsync(CancellationToken cancellationToken)
{
return await Task.Run(() =>
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
// Try receiving for one second
IMessage message = consumer.Receive(TimeSpan.FromSeconds(1));
if (message != null)
{
return message;
}
}
}, cancellationToken).ConfigureAwait(false);
}
Aborting a thread is generally considered bad practice since you can leak resources, so the timeout seems like the only way to cleanly stop a thread. So I have three questions:
What is a generally accepted timeout value for "immediate" cancellation?
For libraries that provide built-in async methods, does immediate cancellation truly exist or do they also use timeouts and loops to simulate it? Maybe the question here is how would you make use of software interrupts and if these also have to do some sort of polling to check if there are interrupts, even if it's at the kernel/CPU level.
Is there some alternate way I should be approaching this?
Edit: So I may have found part of my answer with Thread.Interrupt() and then handling ThreadInterruptedException. Is this basically a kernel-level software interrupt and as close to "immediate" as we can get? Would the following be a better way of handling this?
public async Task<IMessage> ReceiveAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var completionSource = new TaskCompletionSource<IMessage>();
var receiverThread = new Thread(() =>
{
try
{
completionSource.SetResult(consumer.Receive());
}
catch (ThreadInterruptedException)
{
completionSource.SetCanceled();
}
catch (Exception ex)
{
completionSource.SetException(ex);
}
});
cancellationToken.Register(receiverThread.Interrupt);
receiverThread.Name = "Queue Receive";
receiverThread.Start();
return await completionSource.Task.ConfigureAwait(false);
}
It depends on your specific needs. A second could be immediate for some and slow for others.
Libraries (good ones) which provide async API do so from the bottom up. They usually don't wrap blocking (synchronous) operations with a thread to make them seem asynchronous. They use TaskCompletionSource to create truly async methods.
I'm not sure what you mean by queue (the built-in Queue in .Net doesn't have a Receive method) but you should probably be using a truly async data structure like TPL Dataflow's BufferBlock.
About your specific code sample.
You are holding up a thread throughout the entire operation (that's async over sync) which is costly. You could instead try to consume quickly and then wait asynchronously for the timeout to end, or for the CancellationToken to be cancelled.
There's also no point in using another thread with Task.Run. You can simply have the async lambda be the content of ReceiveAsync:
public async Task<IMessage> ReceiveAsync(CancellationToken cancellationToken)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
// Try receiving for one second
IMessage message;
if (!consumer.TryReceive(out message))
{
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
if (message != null)
{
return message;
}
}
}
If your queue implements IDisposable a different (harsher) option would be to call Dispose on it when the CancellationToken is cancelled. Here's how.
I am developing android messanger app based on xamarin and .net 5 async/awaits.
In my app i have producer/consumer pattern for processing messages which is made on infinite loops.
for example ReadTcpClientAsync producer:
async Task ReadTcpClientAsync(CancellationToken cancellationToken)
{
cde.Signal();
while (!cancellationToken.IsCancellationRequested)
{
byte[] buffer = await atc.ReadAsync(cancellationToken);
// queue message...
}
}
or SendStatementsAsync consumer which deque messages and awaits WriteAsync
private async Task SendStatementsAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var nextItem = await _outputStatements.Take();
cancellationToken.ThrowIfCancellationRequested();
// misc ...
await atc.WriteAsync(call.Serialize());
}
}
and some consumers just await on Take calls
var update = await _inputUpdateStatements.Take();
this construction works pretty well on tests, but there is one method where i think i made a huge mistake.
this method intent to run entire client backend, starting 3 pro/con while (true) loops simultaneously.
here it is:
public async Task RunAsync()
{
_isRunning = true;
_progress.ProgressChanged += progress_ProgressChanged;
await InitMTProto(_scheme).ConfigureAwait(false); // init smth...
// various init stuf...
await atc.ConnectAsync().ConfigureAwait(false); // open connection async
// IS IT WRONG?
try
{
await Task.WhenAny(SendStatementsAsync(_cts.Token),
ReadTcpClientAsync(_cts.Token),
ProcessUpdateAsync(_cts.Token, _progress)).ConfigureAwait(false);
}
catch (OperationCanceledException oce)
{
}
catch (Exception ex)
{
}
}
Forget about android for now, think any UI (WinForm, WPF, etc) OnCreate method in UI context to call RunAsync
protected async override void OnCreate(Bundle bundle)
{
// start RA
await client.RunAsync()
// never gets here - BAD, but nonblock UI thread - good
Debug.WriteLine("nevar");
}
so, as you can see there is a problem. I can't do anything after RunAsync await call because it will never returns from Task.WhenAny(...). And i need perform status check there, but i need this pro/cons methods started, because my check wait on ManualResetEvent for it:
if (!cde.Wait(15000))
{
throw new TimeoutException("Init too long");
}
Also, my check is async too, and it works like a charm :)
public async Task<TLCombinatorInstance> PerformRpcCall(string combinatorName, params object[] pars)
{
// wait for init on cde ...
// prepare call ...
// Produce
ProduceOutput(call);
// wait for answer
return await _inputRpcAnswersStatements.Take();
}
I think i should use another approach for starting this infinite loops, but i already have async Task methods all the way - so i really have no idea what to do.
Any help please?
Ok, after a lot of reading (nothing found) and #svick's advice i decided to call this methods without "await" as separate Task.Run's.
Aso i decided to run it in ThreadPool.
My final code is:
try
{
/*await Task.WhenAny(SendStatementsAsync(_cts.Token),
ReadTcpClientAsync(_cts.Token),
ProcessUpdateAsync(_cts.Token, _progress)).ConfigureAwait(false);*/
Task.Run(() => SendStatementsAsync(_cts.Token)).ConfigureAwait(false);
Task.Run(() => ReadTcpClientAsync(_cts.Token)).ConfigureAwait(false);
Task.Run(() => ProcessUpdateAsync(_cts.Token, _progress)).ConfigureAwait(false);
Trace.WriteLineIf(clientSwitch.TraceInfo, "Worker threads started", "[Client.RunAsync]");
}
Everything works fine as expected..
i'm not sure what problems it will cause in exception handling, as i know they will be lost
Of course such calls produce warning
Because this call is not awaited, execution of the current method
continues before the call is completed. Consider applying the 'await'
operator to the result of the call.
which can be easily suppressed this way
// just save task into variable
var send = Task.Run(() => SendStatementsAsync(_cts.Token)).ConfigureAwait(false);
Also, if anyone know better solution i will be grateful to hear it.