Handling exceptions from the synchronous part of async method - c#

I'm dealing with the situation where the task I start may throw, while still executing synchronously on the initial thread. Something like this, for illustrative purposes:
static async Task TestAsync()
{
var random = new Random(Environment.TickCount).Next();
if (random % 2 != 0)
throw new ApplicationException("1st");
await Task.Delay(2000);
Console.WriteLine("after await Task.Delay");
throw new ApplicationException("2nd");
}
From the calling code, I'd like to be able to catch any exceptions, possibly thrown from the synchronous part (i.e., until await Task.Delay()). Here's how I'm currently doing it:
static void Main(string[] args)
{
try
{
var task = TestAsync();
if (task.IsFaulted)
task.GetAwaiter().GetResult();
Console.WriteLine("TestAsync continues asynchronously...");
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.ToString());
}
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
This works, although it looks a bit mouthful, as there is no Result on the Task.
I've also tried task.Wait() instead of task.GetAwaiter().GetResult(). That always gives me AggregateException which I have to unwrap (rather than expected ApplicationException directly).
Is there any other options?
[EDITED] To address the comments: I do this, because if the task fails instantly, I don't want to add it to the list of the pending tasks I maintain. The task itself knows nothing about such a list (and it doesn't have to). I still want to log the exception, and make user aware of it. I could also do throw task.Exception, but that wouldn't give the exception stack frame captured with ExceptionDispatchInfo.
[UPDATE] Inspired by other answers and comments: if I have full control over TestAsync and I don't want introducing new class members, I also could do something like below. It might come handy when validating arguments:
static Task TestAsync(int delay)
{
if (delay < 0)
throw new ArgumentOutOfRangeException("delay");
Func<Task> asyncPart = async () =>
{
Console.WriteLine("await Task.Delay");
await Task.Delay(delay);
throw new ApplicationException("2nd");
};
return asyncPart();
}

I'd split it into two parts, rather than relying on task.GetAwaiter().GetResult() to work. I'd be afraid that someone maintaining TestAsync could unwittingly break things in the future.
This is how I would write it. This should preserve the behavior you've got, but I find it more obvious what's going on:
static Task Test()
{
var random = new Random(Environment.TickCount).Next();
if (random % 2 != 0)
throw new ApplicationException("1st");
return TestAsync();
}
static async Task TestAsync()
{
await Task.Delay(2000);
Console.WriteLine("after await Task.Delay");
throw new ApplicationException("2nd");
}
static void Main(string[] args)
{
try
{
Test();
Console.WriteLine("TestAsync continues asynchronously...");
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.ToString());
}
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}

Generally speaking, exceptions should not be used for routine error handling in your application. Throw exceptions for "exceptional" circumstances, where the program cannot continue because something unexpected happened and you need to do a hard stop.
Granted, I do not know precisely what your use case is, but whenever I'm using async tasks, the piece that is going to fail unexpectedly is usually also the piece that should be asynchronous (e.g. connecting to a database).
Regardless, how I would do this is to put your TestAsync method into its own class. Then, you can have a method (or property) bool TestAsync.IsValid to determine whether the task is ready to go and should be queued for execution; then, you can run your async task if the answer is true: TestAsync.RunAsync().

Related

Stuck with async await thread

In constructor I want to call one method type :
private async Task OnLoadPrometDanKorisnikDatum
and I want to wait that method while its finish, and I have more method(3) like this and I want to call this 3 methods in background thread and don't wait him to finish, just want to wait first method. And I want to them executing parallel.
I have methods async Task,and in constructor of view model I call like this
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat,
DatumVrednost).Wait();
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).Wait();
if I don't place .Wait() on the end, program doesn't work. I see in debug mode they run asynchronly, but time spent tell me that they sub(one method time + second method time + ....).
Can someone help me, this is for me very stuf...
Answer
The best way to handle your scenario is to use async void.
I recommend first reading the Explanation section below to fully understand the best practices around async void.
public MyConstructor()
{
ExecuteAsyncMethods();
}
async void ExecuteAsyncMethods()
{
try
{
await OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost);
await OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja);
}
catch(Exception e)
{
//Handle Exception
}
}
Explanation
Many C# devs are taught "Never use async void", but this is one of the few use-cases for it.
Yes async void can be dangerous and here's why:
Cannot await an async avoid method
Can lead to race conditions
Difficult to catch an Exception thrown by async void methods
E.g. the following try/catch block will not catch the Exception thrown here:
public MyConstructor()
{
try
{
//Cannot await `async void`
AsyncVoidMethodWithException();
}
catch(Exception e)
{
//Will never catch the `Exception` thrown in `AsyncVoidMethodWithException` because `AsyncVoidMethodWithException` cannot be awaited
}
//code here will be executing by the time `AsyncVoidMethodWithException` throws the exception
}
async void AsyncVoidMethodWithException()
{
await Task.Delay(2000);
throw new Exception();
}
That being said, as long as we wrap the contents of our entire async void in a try/catch block, we will be able to catch the exception, like so:
public MyConstructor()
{
AsyncVoidMethodWithException();
}
async void AsyncVoidMethodWithException()
{
try
{
await Task.Delay(2000);
throw new Exception();
}
catch(Exception e)
{
//Exception will be caught and successfully handled
}
}
SafeFireAndForget
I created a library to help with this and its additional benefit is that it avoids writing async void code that could be potentially misused by future devs.
It's open source and also available on NuGet:
Source Code
NuGet Package
SafeFireAndForget
SafeFireAndForget allows us to safely execute a Task whilst not blocking the calling thread and without waiting for it to finish before moving to the next line of code.
Below is a simplified version of SafeFireAndForget that you can add to your project.
However, I recommend copy/pasting its complete source code or adding its NuGet Package to your library to get a more robust implementation
public static async void SafeFireAndForget<TException>(this Task task, Action<TException> onException = null, bool continueOnCapturedContext = false) where TException : Exception
{
try
{
await task.ConfigureAwait(continueOnCapturedContext);
}
catch (TException ex) when (onException != null)
{
onException(ex);
}
}
Using SafeFireAndForget
To use SafeFireAndForget, append it to your method call like so:
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost).SafeFireAndForget();
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).SafeFireAndForget();
To handle any Exception thrown by that Task, use onException. Here's an example that prints the Exception to the Debug Console:
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost).SafeFireAndForget(ex => Debug.WriteLine(ex));
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).SafeFireAndForget(ex => Debug.WriteLine(ex));

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.

Timeout for asynchronous Task<T> with additional exception handling

In my project, I reference types and interfaces from a dynamic link library.
The very first thing I have to do when using this specific library is to create an instance of EA.Repository, which is defined within the library and serves as kind of an entry point for further usage.
The instantiation EA.Repository repository = new EA.Repository() performs some complex stuff in the background, and I find myself confronted with three possible outcomes:
Instantiation takes some time but finishes successfully in the end
An exception is thrown (either immediately or after some time)
The instantiation blocks forever (in which case I'd like to cancel and inform the user)
I was able to come up with an asynchronous approach using Task:
public static void Connect()
{
// Do the lengthy instantiation asynchronously
Task<EA.Repository> task = Task.Run(() => { return new EA.Repository(); });
bool isCompletedInTime;
try
{
// Timeout after 5.0 seconds
isCompletedInTime = task.Wait(5000);
}
catch (Exception)
{
// If the instantiation fails (in time), throw a custom exception
throw new ConnectionException();
}
if (isCompletedInTime)
{
// If the instantiation finishes in time, store the object for later
EapManager.Repository = task.Result;
}
else
{
// If the instantiation did not finish in time, throw a custom exception
throw new TimeoutException();
}
}
(I know, you can probably already spot a lot of issues here. Please be patient with me... Recommendations would be appreciated!)
This approach works so far - I can simulate both the "exception" and the "timeout" scenario and I obtain the desired behavior.
However, I have identified another edge case: Let's assume the instantiation task takes long enough that the timeout expires and then throws an exception. In this case, I sometimes end up with an AggregateException saying that the task has not been observed.
I'm struggling to find a feasible solution to this. I can't really cancel the task when the timeout expires, because the blocking instantiation obviously prevents me from using the CancellationToken approach.
The only thing I could come up with is to start observing the task asynchronously (i.e. start another task) right before throwing my custom TimeoutException:
Task observerTask = Task.Run(() => {
try { task.Wait(); }
catch (Exception) { }
});
throw new TimeoutException();
Of course, if the instantiation really blocks forever, I already had the first task never finish. With the observer task, now I even have two!
I'm quite insecure about this whole approach, so any advice would be welcome!
Thank you very much in advance!
I'm not sure if I fully understood what you're trying to achieve, but what if you do something like this -
public static void Connect()
{
Task<EA.Repository> _realWork = Task.Run(() => { return new EA.Repository(); });
Task _timeoutTask = Task.Delay(5000);
Task.WaitAny(new Task[]{_realWork, timeoutTask});
if (_timeoutTask.Completed)
{
// timed out
}
else
{
// all good, access _realWork.Result
}
}
or you can even go a bit shorter -
public static void Connect()
{
Task<EA.Repository> _realWork = Task.Run(() => { return new EA.Repository(); });
var completedTaskIndex = Task.WaitAny(new Task[]{_realWork}, 5000);
if (completedTaskIndex == -1)
{
// timed out
}
else
{
// all good, access _realWork.Result
}
}
You can also always call Task.Run with a CancellationToken that will time out, but that will raise an exception - the above solutions give you control of the behaviour without an exception being thrown (even though you can always try/catch)
Here is an extension method that you could use to explicitly observe the tasks that may fail while unobserved:
public static Task<T> AsObserved<T>(this Task<T> task)
{
task.ContinueWith(t => t.Exception);
return task;
}
Usage example:
var task = Task.Run(() => new EA.Repository()).AsObserved();

Asyncronous console with task controlling

Please help me find the right solution.
The main problem the waiting completion of the program through the console, and at the same time monitor tasks.
I wrote some prototype, but I am not sure that is effective - in this approach, we spend an extra thread of waiting for action from the console. I do not see alternatives, since Console does not support asynchronous (some sort of Console.ReadLineAsync).
UPDATE:
I have two working Tasks (task1, task2).They simulate some real work.
The program is a console. So we need to give user a chance to stop the program.By default in consoles, this is done via the expectation of pressing "Enter" (through consoleTask).
The question is. How to wait for the completion of worker threads AND Monitor stop command from the user.
static void Main(string[] args)
{
CancellationTokenSource mycts = new CancellationTokenSource();
var task1 = Task.Run(() =>
{
// doing some work, that can throw exception
Thread.Sleep(1000);
// how to avoid this closuring ?
mycts.Token.ThrowIfCancellationRequested();
throw new InvalidOperationException("test");
}).ContinueWith((_) => mycts.Cancel()); // Do I need caching this task?
var task2 = Task.Run(() =>
{
// doing some work, that can throw exception
Thread.Sleep(5000);
// again closuring
mycts.Token.ThrowIfCancellationRequested();
throw new InvalidOperationException("test");
}).ContinueWith((_) => mycts.Cancel()); // Do I need caching this task?
// I do not know how to do better with Console !!
var consoleTask = Task.Factory.StartNew((cts) =>
{
Console.WriteLine("Press Enter to exit");
Console.ReadLine();
}, mycts).ContinueWith((_) => mycts.Cancel()); // Do I need caching this task?
// Waiting for the Completion or Exception
Task.WaitAny(task1, task2, consoleTask);
// Now waiting for the completion of workflow
try
{
Task.WaitAll(task1, task2);
}
catch (Exception ex)
{
// log faulted tasks
}
//Exit
}
There's a few guidelines you should follow:
Do not use ContinueWith. Use await instead.
Do not use Task.Factory.StartNew. Use Task.Run instead.
Do not mix blocking and asynchronous code. In the case of a console application, it's generally best to just have Main call a MainAsync and wait on the returned task. For most applications, that's the only blocking you should use.
I'm not sure what how to avoid this closuring? means.
In the case of ReadLine (and other Console methods), you are correct, there are unfortunately no asynchronous methods. It might work to use a separate thread, but the Console class (more particularly, the Console input and output streams) have some unusual locking going on under the covers, so I'm not positive this would work:
static void Main(string[] args)
{
MainAsync().Wait();
}
static CancellationTokenSource mycts = new CancellationTokenSource();
static async Task MainAsync()
{
try
{
var task1 = CancelAfterSuccessfulCompletionAsync(
Task.Run(() => SomeWorkThatCanThrowException()));
var task2 = CancelAfterSuccessfulCompletionAsync(
Task.Run(() => OtherWorkThatCanThrowException()));
var consoleTask = CancelAfterSuccessfulCompletionAsync(
Task.Run(() => MonitorConsole()));
await Task.WhenAll(task1, task2, consoleTask);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
static void OtherWorkThatCanThrowException()
{
Thread.Sleep(5000);
mycts.Token.ThrowIfCancellationRequested();
throw new InvalidOperationException("test");
}
static void SomeWorkThatCanThrowException()
{
Thread.Sleep(1000);
mycts.Token.ThrowIfCancellationRequested();
throw new InvalidOperationException("test");
}
static void MonitorConsole()
{
Console.WriteLine("Press Enter to exit");
Console.ReadLine();
}
static async Task CancelAfterSuccessfulCompletionAsync(Task task)
{
await task;
mycts.Cancel();
}
Since console doesn't have a SynchronizationContext there's not much you can do without blocking the main thread while your async operations are executing.
However, it's much simpler if you just write your code as if it's asynchronous and block in the simplest possible way. I would suggest moving all your code into an async MainAsync and blocking once:
static void Main()
{
MainAsync().Wait();
}
static async Task MainAsync()
{
// manage tasks asynchronously
}
What you can do, instead of blocking, is use a custom context for executing asynchronous operations like Stephen Cleary's AsyncContext. This allows you to avoid blocking synchronously on a Task:
static void Main()
{
AsyncContext.Run(MainAsync);
}

TaskCanceledException with ContinueWith

I've been trying to figure out why I'm getting a TaskCanceledException for a bit of async code that has recently started misbehaving. I've reduced my issue down to a small code snippet that has me scratching my head:
static void Main(string[] args)
{
RunTest();
}
private static void RunTest()
{
Task.Delay(1000).ContinueWith(t => Console.WriteLine("{0}", t.Exception), TaskContinuationOptions.OnlyOnFaulted).Wait();
}
As far as I'm aware, this should simply pause for a second and then close. The ContinueWith won't be called (this only applies to my actual use-case). However, instead I'm getting a TaskCanceledException and I've no idea where that is coming from!
You are using the wrong taskcontinuationoption:
See following link : https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskcontinuationoptions%28v=vs.110%29.aspx
It says :
Specifies that the continuation task should be scheduled only if its antecedent threw an unhandled exception. This option is not valid for multi-task continuations.
As guys said above this call requires just antecedent-task in faulted-status otherwise will throw TaskCanceledException, for this concrete case you can generalize ContinueWith to process all statuses:
await Task.Delay(1000).ContinueWith(
task =>
{
/* take into account that Canceled-task throw on next row the TaskCancelledException */
if (!task.IsFaulted) {
return;
}
Console.WriteLine("{0}", task.Exception);
// do smth like 'throw task.Exception.InnerException'
});
I also received this error:
The block of code looked like this:
private void CallMediator<TRequest>(TRequest request) where TRequest : IRequest<Unit>
{
_ = Task.Run(async () =>
{
var mediator = _serviceScopeFactory.CreateScope().ServiceProvider.GetService<IMediator>()!;
await mediator.Send(request).ContinueWith(LogException, TaskContinuationOptions.OnlyOnFaulted);
});
}
private void LogException(Task task)
{
if (task.Exception != null)
{
_logger.LogError(task.Exception, "{ErrorMessage}", task.Exception.Message);
}
}
Reading the documentation for the ContinueWith method, it has the following remarks:
The returned Task will not be scheduled for execution until the current task has completed. If the continuation criteria specified through the continuationOptions parameter are not met, the continuation task will be canceled instead of scheduled.
So for me, it called the first task (mediator.Send(request)), then it continued with the task ContinueWith(...), which is the one I awaited. However, since an exception had not occurred in the first task, the second task was cancelled. Therefore, when awaiting the second task, it threw a TaskCanceledException.
What I did, was to change the code to this:
private void CallMediator<TRequest>(TRequest request) where TRequest : IRequest<Unit>
{
_ = Task.Run(async () =>
{
var mediator = _serviceScopeFactory.CreateScope().ServiceProvider.GetService<IMediator>()!;
try
{
_ = await mediator.Send(request);
}
catch (Exception ex)
{
_logger.LogError(ex, "{ErrorMessage}", ex.Message);
}
});
}
Instead of using .ContinueWith(...), I have replaced it with just a regular try-catch block in case of the task I am interested in fails. I think this simplifies the code and makes it more readable.
In the question, there is this line of code:
Task.Delay(1000).ContinueWith(t => Console.WriteLine("{0}", t.Exception), TaskContinuationOptions.OnlyOnFaulted).Wait();
I would rewrite it to:
try
{
Task.Delay(1000).Wait();
}
catch (Exception ex)
{
Console.WriteLine("{0}", ex);
}

Categories