Capturing Exceptions on async operations - c#

I'm reading up more about async here: http://msdn.microsoft.com/en-us/library/hh873173(v=vs.110).aspx
Going through this example:
Task<bool> [] recommendations = …;
while(recommendations.Count > 0)
{
Task<bool> recommendation = await Task.WhenAny(recommendations);
try
{
if (await recommendation) BuyStock(symbol);
break;
}
catch(WebException exc)
{
recommendations.Remove(recommendation);
}
}
I wonder, if I'm already performing await on Task.WhenAny why do I need to await again inside of the try block?
If I already did this: Task<bool> recommendation = await Task.WhenAny(recommendations);
Why do this: if (await recommendation) BuyStock(symbol);

The first await exists to asynchronously wait for the first task to complete (i.e. recommendation).
The second await is only there to extract the actual result out of the already completed task, and throw exceptions stored in the task. (it's important to remember that awaiting a completed task is optimized and will execute synchronously).
A different option to get the result would be using Task<T>.Result, however it differs in the way it handles exceptions. await would throw the actual exception (e.g WebException) while Task<T>.Result would throw an AggregateException containing the actual exception inside.
Task<bool> [] recommendations = …;
while(recommendations.Count > 0)
{
Task<bool> recommendation = await Task.WhenAny(recommendations);
try
{
if (recommendation.Result)
{
BuyStock(symbol);
}
break;
}
catch(AggregateException exc)
{
exc = exc.Flatten();
if (exc.InnerExceptions[0] is WebException)
{
recommendations.Remove(recommendation);
}
else
{
throw;
}
}
}
Clearly awaiting the task is simpler and so it's the recommended way of retrieving a result out of a task.

The use of await here creates the desired error handling semantics. If he used Result instead of await then the AggregateException would be rethrown directly; when using await the first exception within the AggregateException is pulled out an that exception is re-thrown. Clear the author of this code wanted the WebException to be thrown, rather than an AggregateException that he would need to manually unwrap.
Could he have used another approach, sure. This was simply the approach that the author of the code preferred, as it allows him to write the code more like traditional synchronous code rather than radically changing the style of the code.

You're right. It is not necessary. You could replace it with
if (recommendation.Result)
BuyStock(symbol);
Also note that await will not await(will not set continuation) when completed task is given. It will just execute synchronously in that case as a optimization. I guess author leverages that optimization.
If you ask why author wrote that way, May be consistency? only he knows!.

If I already did this: Task recommendation = await Task.WhenAny(recommendations); Why do this: if (await recommendation) BuyStock(symbol);
Because Task.WhenAny returns a Task<Task<bool>> and you want to unwrap the outter Task to retrieve the resulting bool. You could do the same by accessing the Task.Result property of the returned Task

Other answers have pointed out that you have to await the task returned by await Task.WhenAll to unwrap the return value (alternatively, you can use the Result property).
However, you can also get rid of your try/catch (and it's a good thing to avoid catching unnecessary exception)
Task<bool> recommendation = await Task.WhenAny(recommendations);
if(!recommendation.IsFaulted)
{
if (await recommendation) BuyStock(symbol);
break;
}
else
{
if(recommendation.Exception.InnerExceptions[0] is WebException)
{
recommendations.Remove(recommendation);
}
else
{
throw recommendation.Exception.InnerExceptions[0];
}
}

Because Task.WhenAny<TResult>(IEnumerable<Task<TResult>> tasks) returns a Task<Task<TResult>>. The outer task (the one created by the Task.WhenAny call) will complete when any of the tasks passed to it completes with the completed task as a result.

Related

How to make sure a task is done before returning from a function?

I have a decent understanding of how Async/Await works in C#.
I understand that when the await keyword is reached, control is given back to the calling function and that is how asynchronous execution is achieved (if I am wrong here, I would appreciate correction and a better explanation).
However I am not sure as to how I would make sure that an async function has finished executing before returning from a function.
Take the example below:
private static async Task<bool> LoadDataIntoLocalAppFile(ZipArchiveEntry entry, string key)
{
try
{
/* SqLiteAsyncConnection is an instance variable in the class that has this function.*/
string pathToUse = "localDatabase.db"
if (SqLiteAsyncConnection != null)
{
await SqLiteAsyncConnection.CloseAsync()
.ContinueWith(x => entry.ExtractToFile(pathToUse, true));
}
else
{
entry.ExtractToFile(pathToUse, true);
}
return true;
}
catch (Exception ex)
{
Colsole.WriteLine(ex.ToString());
return false;
}
}
In the above code snippet, I want to make sure that my SqLiteAsyncConnection is closed before I replace the data in the .db file with the contents of entry (so that it does not error out). As such I have used ContinueWith (if this is wrong I would appreciate some clarification).
But I also want to ensure that await SqLiteAsyncConnection.CloseAsync().ContinueWith(x => entry.ExtractToFile(pathToUse, true)); finishes its execution before the function returns. That is I want to ensure that this function does not return true inaccurately, and that it does not return true before await SqLiteAsyncConnection.CloseAsync() .ContinueWith(x => entry.ExtractToFile(pathToUse, true)); finishes its execution.
How do I achieve this?
You don't need the ContinueWith when you have await there. You would use ContinueWith if you don't use await, it can be seen as a type of callback once the parent task has completed/failed.
await SqLiteAsyncConnection.CloseAsync()
.ContinueWith(x => entry.ExtractToFile(pathToUse, true));
//can be replaced by
await SqLiteAsyncConnection.CloseAsync();
entry.ExtractToFile(pathToUse, true);
When you hit an await section in your code, your function isn't done, it doesn't return true, it gets suspended in a sense and the control is given back to calling thread. Once all your awaits have completed in the function, only then does it return the result.
When the await keyword is reached, control is given back to the calling function.
I find this commonly used explanation confusing. In simpler terms, the job of an asynchronous method is to create a Task. That's why you call the method, to get a Task back. You get this Task when the first¹ await is reached inside this method. At this point the asynchronous operation has started, and (generally) it has not completed yet. The caller has received the Task that represents the asynchronous operation, and can continue doing other things while the operation is in-flight. In some cases the caller has nothing better to do than wait for the completion of the asynchronous operation, in which case you see this pattern:
await LoadDataIntoLocalAppFile();
Here the caller is itself an asynchronous method. We know it because of the await keyword. The code above is equivalent with this:
Task task = LoadDataIntoLocalAppFile();
await task;
The first line will complete when the first await inside the LoadDataIntoLocalAppFile is reached. The second line will complete when the last line of code inside the LoadDataIntoLocalAppFile has ran.
¹ To be more precise, the first await of a non-completed awaitable.
Your understanding of async/await is incorrect, just awaiting the asynchronous call is enough:
private static async Task<bool> LoadDataIntoLocalAppFile(ZipArchiveEntry entry, string key)
{
try
{
string pathToUse = "localDatabase.db"
await SqLiteAsyncConnection?.CloseAsync();
entry.ExtractToFile(pathToUse, true);
return true;
}
catch (Exception ex)
{
Colsole.WriteLine(ex.ToString());
return false;
}
}
Then call with:
await LoadDataIntoLocalAppFile(...

Task.WhenAll().ContinueWith() throws unexpected Task Canceled exception

I am using Task.WhenAll to schedule tasks concurrently (more or less of course) and want to rethrow all exceptions rather than the first one what Task.WhenAll would do.
So I want to perfor a continuation whenever any child task faulted or has been canceled.
That's what NotOnRanToCompletion means, right?
Well this actually works but in case that all Tasks actually have completed,
I get a Task Cancelled exception on the Continuation?
var semaphore = new SemaphoreSlim(10);
var tasks = portList.Select(async port =>
{
try
{
await semaphore.WaitAsync();
await this.AddDeviceAsync(bioprocessDevicesDto, template, port);
}
finally
{
semaphore.Release();
}
});
await Task.WhenAll(tasks).ContinueWith(t =>
{
// this will make all inner exceptions available
// never null
throw t.Exception;
}, TaskContinuationOptions.NotOnRanToCompletion).ConfigureAwait(false);
That behavior is by-design and documented.
Note that your code is not awaiting the task returned from WhenAll; it's awaiting the continuation from ContinueWith.
ContinueWith is a low-level method with dangerous default behavior. Use await instead:
var tasks = portList.Select(...);
var allTask = Task.WhenAll(tasks);
try { await allTask; }
catch { throw allTask.Exception; }
When you look at the TaskContinuationOptions for NotOnRanToCompletion, it states:
This option is not valid for multi-task continuations.
I would try changing NotOnRanToCompletion to another option that handles multi-task continuations.

Concise way to await a canceled Task?

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.

Return with await when wrapping old async pattern into TaskCompletionSource?

I am studying C# Asnc-await pattern and currently reading Concurrency in C# Cookbook from S. Cleary
He discusses wrapping old non TAP async patterns with TaskCompletionSource (TCS) into TAP constructs.
What I dont get is, why he just returns the Task property of the TCS object instead of awaiting it TCS.Task ?
Here is the example code:
Old method to wrap is DownloadString(...):
public interface IMyAsyncHttpService
{
void DownloadString(Uri address, Action<string, Exception> callback);
}
Wrapping it into TAP construct:
public static Task<string> DownloadStringAsync(
this IMyAsyncHttpService httpService, Uri address)
{
var tcs = new TaskCompletionSource<string>();
httpService.DownloadString(address, (result, exception) =>
{
if (exception != null)
tcs.TrySetException(exception);
else
tcs.TrySetResult(result);
});
return tcs.Task;
}
Now why not just do it that way:
public static async Task<string> DownloadStringAsync(
this IMyAsyncHttpService httpService, Uri address)
{
var tcs = new TaskCompletionSource<string>();
httpService.DownloadString(address, (result, exception) =>
{
if (exception != null)
tcs.TrySetException(exception);
else
tcs.TrySetResult(result);
});
return await tcs.Task;
}
Is there a functional difference between the two? Is the second one not more natural?
By marking it async, the compiler will generate warnings, that it should be considered to await this method
You don't have to mark your own method as async in order to get the "Task not awaited" warning. The following code generates the same warning for the calls to both T and U:
static async Task Main(string[] args)
{
Console.WriteLine("Done");
T();
U();
Console.WriteLine("Hello");
}
public static Task T()
{
return Task.CompletedTask;
}
public static async Task U()
{
await Task.Yield();
return;
}
Whenever you find yourself with a method only containing a single await and that being the last thing it does (except possibly returning the awaited value), you should ask yourself what value it's adding. Aside from some differences in exception handing, it's just adding an extra Task into the mix.
await is generally a way of indicating "I've got no useful work to do right now, but will have when this other Task is finished" which of course isn't true (you've got no other work to do later). So skip the await and just return what you would have awaited instead.
Your version is strictly more complex -- instead of just returning the task, you make the method async, and await the task you could just be returning.
There is one subtle practical difference (other than the version with await being slower to run).
In the first example, if DownloadString throws an exception (rather than calling the delegate you pass it with exception set), then that exception will bubble through your call to DownloadStringAsync.
In the second, the exception is packaged into the Task returned from DownloadStringAsync.
So, assuming that DownloadString throws this exception (and no other exceptions occur):
Task<string> task;
try
{
task = httpService.DownloadStringAsync(...);
}
catch (Exception e)
{
// Catches the exception ONLY in your first non-async example
}
try
{
await task;
}
catch (Exception e)
{
// Catches the exception ONLY in your second async example
}
You probably don't care about the distinction - if you just write:
await httpService.DownloadStringAsync(...);
you won't notice the difference.
Again, this only happens if the DownloadString method itself throws. If it instead calls the delegate you give it with exception set to a value, then there is no observable difference between your two cases.
Folks, thanks for the helpful comments.
In the meantime I have read the sources referenced here and also investigated the matter further: Influenced by https://blog.stephencleary.com/2016/12/eliding-async-await.html I have come to the conclusion, that its best practice to include async-await by default even in synchronous methods of the async function chain and only omit async await when the circumstances clearly indicate that the method will not potentially behave differently as expected from an async method en edge scenarios.
Such as: the synchronous method is short, simple and has no operations inside which could throw an exception. If the synchronous method throws an exception the caller will get an exception in the calling line instead of the line where the Task is awaited. This is clearly a change from expected behavior.
For example handing over call parameters to the next layer unchanged is a situation which imo permits omitting async-await.
Read my answer why returning the task is not a good idea: What is the purpose of "return await" in C#?
Basically you break your call stack if you don't use await.

Using await inside a ContinueWith() block

I have the following code:
var result = MessageBoxHelper.MsgBox
.ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo)
.ContinueWith((answer) =>
{
if (answer.Result == MessageBoxResult.Yes)
{
Task<bool> asyncTask = ExecuteAsyncFunc();
//asyncTask.Unwrap(); ??
asyncTask.ContinueWith((a) =>
{
// More
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}
Invoked elsewhere like this:
public async Task<bool> ExecuteAsyncFunc()
{
await CallAnotherAsyncFunction();
}
I believe that I need to call Unwrap(), where I have tried to, because I am calling await inside a ContinueWith() block. However, I get the following error when I uncomment it:
Error CS1929 'Task' does not contain a definition for 'Unwrap'
and the best extension method overload
'TaskExtensions.Unwrap(Task)' requires a receiver of type
'Task'
Do I need to use Unwrap in this context, and if so, what am I doing wrong?
The short answer is to use await instead of ContinueWith:
var result = MessageBoxHelper.MsgBox.ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo);
if (answer == MessageBoxResult.Yes)
{
var a = await ExecuteAsyncFunc();
// More
}
The long answer is that ContinueWith has some dangerous default options (including using an ambient TaskScheduler), and await automatically does everything correctly for you. I go into more detail on my blog.
Do I need to use Unwrap in this context, and if so, what am I doing
wrong?
You need to Unwrap only when your return type is a Task<Task>, and you actually want to do something with the inner task.
For example:
Task<Task> exampleTask = Task.Factory.StartNew(async () =>
{
await Task.Delay(1000);
});
If I wanted to actually asynchronously wait on the inner Task, i'd call Unwrap and await on that:
Task exampleTask = await Task.Factory.StartNew(async () =>
{
await Task.Delay(1000);
}).Unwrap();
If for any reason you'd want to await on any task returned from your continuation, or want to monitor the inner task, then you'd call Unwrap. There is no need to do that here.
What you're doing is simply invoking an async method inside your ContinueWith, but it doesn't seem like you want to await the result at all.
I'd recommend using async-await wherever possible to reduce the verbosity of ContinueWith. Mixing those two together usually yields bad results as thing get rather complicated. Refactoring that code ends up much cleaner with much less cyclomatic complexity:
var result = await MessageBoxHelper.MsgBox.ShowAsync("Press Yes to proceed",
MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
{
bool answer = await ExecuteFuncAsync();
}
You say that you have to call unwrap due to your using await inside the ContinueWith; I don't actually see you using await though. I assume what you meant is that you want to await but are not sure how, and thus thought unwrapping is needed. If that is the case, then what you want is to just make your nested Task awaitable. You can do that by making the delegate you provide async
var result = MessageBoxHelper.MsgBox
.ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo)
.ContinueWith(async (answer) =>
{
if (answer.Result == MessageBoxResult.Yes)
{
await ExecuteAsyncFunc();
}
}, TaskContinuationOptions.OnlyOnRanToCompletion);
public async Task<bool> ExecuteAsyncFunc()
{
await CallAnotherAsyncFunction();
}
This allows you to await within the delegate, within the ContinueWith call and not have to deal with unwrapping.
If you are going to do anything with the Task, such as return it without unwrapping, then this is the right way to go.
public Task<BoolOrSomeOtherReturnType> Foo()
{
return MessageBoxHelper.MsgBox
.ShowAsync /* Continue with etc here */
}
But if you are going to act on the results within the Foo method, then there is no need to use ContinueWith, just await it.
public async Task<bool> Foo()
{
var result = await MessageBoxHelper.MsgBox
.ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
{
await ExecuteAsyncFunc();
return true;
}
return false;
}
That simplifies your code quiet a bit. The other thing to note is that your ExecuteAsyncFunc() method does not act on the return value. You simply await and do nothing else.
I noticed that you're not returning true/false, so I assume there is more code there that you just omitted for clarity sake. If that isn't the case, you can save yourself some overhead and just return the Task, allowing someone further up the callstack to await it instead. If your call to CallAnotherAsyncFunction returns Task<bool> then you can just return that call in the same way as well. You only need to await if you have to prevent the method from going any further so you can react to the result of the awaited method. If you don't have to, just return the Task.
public Task<bool> ExecuteAsyncFunc()
{
return CallAnotherAsyncFunction();
}

Categories