Task WhenAll exception - c#

I have to execute about 10 tasks and then obtain their results (all different). I have created a wrapper to catch the exceptions, I don't want to know which one failed, but if a failure it should not continue. I also use WhenAll for this, but the problem is that then I get the results of each one and I get an unhandled exception, is there no way not to continue if there are any exceptions from my wrapper?
var 1Task = api.1TaskAsync();
var 2Task = api.2TaskAsync();
var 3Task = api.3TaskAsync();
await taskService.RunSafeAsync(() => Task.WhenAll(1Task, 2Task, 3Task));
var result = await 1Task; <-------------exception
TaskService:
public async Task RunSafeAsync(Func<Task> task)
{
try
{
await task();
}
catch (Exception ex)
{
}
}

You can't retrieve the result from a function that has faulted. It doesn't have a result-- because it never finished. Attempting to retrieve its result (as you are) is going to throw an exception, because there is nothing to assign to the variable that is supposed to accept the return value.
To avoid this, check if the task is faulted before attempting to retrieve the result.
var 1Task = api.1TaskAsync();
var 2Task = api.2TaskAsync();
var 3Task = api.3TaskAsync();
await taskService.RunSafeAsync(() => Task.WhenAll(1Task, 2Task, 3Task));
if (!1Task.IsFaulted)
{
var result = await 1Task;

If you just want to not continue if there was an exception in any of the methods then don't wrap WhenAll and react if it throws.
try
{
await Task.WhenAll(...);
// None of the tasks threw an exception;
}
catch (Exception ex)
{
// One or more tasks threw an exception
return / throw / etc.
}
//Continue the happy path
Cancellation token
If you need to attempt tasks simultaneously and abort all tasks if one fails than you'll need to look at the cancellation token and activate the token if any exceptions occur (not in the catch for whole WhenAll, but each tasks would have to be executed in a try..catch).
Maybe sequentially?
BTW. With .WhenAll all tasks will be attempted. Maybe you actually want to execute the tasks in sequence and continue only if there are not exceptions:
try
{
await F1();
await F2();
}
catch(Exception ex)
{
}
This doesn't seem slick but it makes it very clear that task 2 will not run unless task 1 is a success.

Related

async/wait: bubbleup exceptions?

This pertains to a program that has to collect some info from a server on the web.
Question: How can I get the exceptions from GetServerResponseAsync and Check to bubble up through CheckAsync to the Main program?
As depicted here, they do not. ErrorHandler never gets hit.
My Main program:
....
try
{
Task.Run(() => CheckAsync());
// bubble to here?
ReadConfiguration();
}
catch (Exception ex)
{
// gather and handle all exceptions here
ErrorHandler.NotifyMe(new[] { "some message" }, ErrorType.Stop); // never gets hit
}
public async Task CheckAsync()
{
await GetServerResponseAsync("slm_check"); // may throw exception
...
if (.....)
throw new Exception("...");
...
}
public async Task GetServerResponseAsync(string command)
{
...
// client = HttpClient()
using (apacheResponse = await client.GetAsync(ServerUrl + "...."))
{
if (....)
throw new Exception("Internal web server error", new Exception("Maybe MySQL server is down"));
using (HttpContent content = apacheResponse.Content)
{
if ( ....)
throw new Exception("error message");
}
}
}
How can I get the exceptions from GetServerResponseAsync and Check to bubble up through CheckAsync to the Main program?
Use await to consume your tasks, instead of ignoring them.
Specifically, this line:
Task.Run(() => CheckAsync());
is getting a Task back from the Task.Run method, which is then ignored. Instead of ignoring that task, the code should be awaiting it:
await Task.Run(() => CheckAsync());
As other commenters have pointed out, the Task.Run here doesn't really make sense. If your operation is asynchronous, it shouldn't need to also run on a background thread. Usually. :) So if you take out the Task.Run, your code would look like:
await CheckAsync();
which will properly propagate the exception.

Trying to understand Task.ContinueWith()

I'm trying to understand some (in my eyes) weird behaviour. I have a call to some async method and want to retrieve its result. (DeleteIndexAsync return a Task<bool>)
var deleteTask = Task.Run(() => DeleteIndexAsync(localItem))
.ContinueWith(t =>
{
//handle and log exceptions
}, TaskContinuationOptions.OnlyOnFaulted);
if (!deleteTask.Result)
In this scenario Result is false and the Status is WaitingForActivation.
Whereas this code does what I want
var deleteTask = Task.Run(() => DeleteIndexAsync(localItem));
deleteTask.ContinueWith(t =>
{
//handle and log exceptions
return false;
}, TaskContinuationOptions.OnlyOnFaulted);
if (!deleteTask.Result)
Can someone explain why? And is it possible to use async / await instead of Task here?
Edit:
var deleteTask = Task.Run(() => ThrowEx());
bool errorOccurred = false;
deleteTask.ContinueWith(t =>
{
errorOccurred = true;
}, TaskContinuationOptions.OnlyOnFaulted);
if (errorOccurred)
{
return true;
}
If you chain the calls, like in the first example, the value you are assigning to the deleteTask variable is actually the second Task. This is the one that is supposed to run only on failure of the first task (the one calling DeleteIndexAsync).
This is because both Task.Run and Task.ContinueWith return Tasks that they create. It explains why in the first example you get Status == WaitingForActivation. In the first snippet, accessing deleteTask.Result would cause an exception to be thrown. In case DeleteIndexAsync threw, it would be an AggregateException containing original exception (unless you accessed t.Exception), otherwise it would be stating that the "Operation was cancelled" - this is because you try to get the result of the task that was scheduled conditionally and the condition was not met.
If you made method containing the snipped async you could do it like this (not tested):
bool success = false;
try
{
success = await DeleteIndexAsync(localItem);
}
catch (Exception) {}
if (!success)
{
//TODO: handler
}
Regarding question edit:
Using captured variable should help, but your current solution introduces race condition. In this case it would be better to do like this:
var deleteTask = Task.Run(() => ThrowEx());
try
{
deleteTask.Wait();
}
catch (Exception ex)
{
return true;
}
but at this point you can drop the asynchronous call entirely, because you wait for the result immediately - unless this example simplifies work that can be done between Run and Wait)
I have a call to some async method and want to retrieve its result.
The best way to do this is with await, not Result.
Can someone explain why?
In the first example, deleteTask is the task returned from Task.Run. In the second example, deleteTask is the task returned from ContinueWith. This task never actually executes unless DeleteIndexAsync throws an exception.
And is it possible to use async / await instead of Task here?
Yes, that's the best approach. When working with asynchronous code, you should always use await instead of ContinueWith. In this case, the TaskContinuationOptions.OnlyOnFaulted means you should put the continuation code in a catch block after the await:
bool deleteResult;
try
{
deleteResult = await Task.Run(() => DeleteIndexAsync(localItem));
}
catch (Exception ex)
{
//handle and log exceptions
}
if (!deleteResult)
...
Or, since it looks like DeleteIndexAsync is asynchronous, removing Task.Run would be more appropriate:
bool deleteResult;
try
{
deleteResult = await DeleteIndexAsync(localItem);
}
catch (Exception ex)
{
//handle and log exceptions
}
if (!deleteResult)
...

Getting the completed task Id using Task.WaitAny()

I know that my goal can be achieved by using Task.WhenAny() but I will not deal with async await if I can avoid it, in order to prevent deadlocks. I have following code:
try
{
Task.WaitAny(this.Tasks.ToArray());
foreach (var task in this.Tasks)
{
if (task.IsFaulted || task.IsCanceled)
{
if (task.Exception.InnerException is OperationCanceledException)
{
}
}
}
}
catch (OperationCanceledException o)
{
// Handling cancelled tasks
}
catch (Exception e)
{
// Handling faulted tasks
}
And I for instance want to know exactly the id of my task, which has faulted or the Id of my task which has been cancelled. I have tried to do so as shown in the try block above, but this is not a solution since it also will throw an exception for tasks that has been cancelled before. Can I obtain a solution for this problem using Task.WaitAny() ?.
From the documentation of Task.WaitAny:
Return Value
Type: System.Int32
The index of the completed Task object in the tasks array.
So you can do this:
var taskIndex = Task.WaitAny(this.Tasks.ToArray());
var task = this.Tasks[taskIndex];

Exception is not caught at Cancelation of Task.Run

I have a class Worker which is doing some work (with simulated workload):
public class Worker
{ ...
public void DoWork(CancellationToken ct)
{
for (int i = 0; i < 10; i++)
{
ct.ThrowIfCancellationRequested();
Thread.Sleep(2000);
}
}
Now I want to use this method in a Task.Run (from my Windows Forms App,at button-click) which can be cancelled:
private CancellationTokenSource _ctSource;
try
{
Task.Run(() =>
{
_worker.DoWork(_ctSource.Token);
},_ctSource.Token);
}
catch (AggregateException aex)
{
String g = aex.Message;
}
catch (OperationCanceledException ex)
{
String g = ex.Message;
}
catch (Exception ex)
{
String g = ex.Message;
}
But when the task is started, I can't cancel it with _ctSource.Cancel();
I get an error in visual studio that the OperationCanceledException is not handled!
But I surrounded the Task.Run Call in a try-catch-clause! The Exception which ocurrs in the Worker object should thrown up or not?
What is the problem?
Your Task.Run call creates the task and then returns immediately. It doesn't ever throw. But the task it creates may fail or be canceled later on.
You have several solutions here:
Use await:
await Task.Run(...)
Attach a continuation depending on the failure/cancellation case:
var task = Task.Run(...);
task.ContinueWith(t => ..., TaskContinuationOptions.OnlyOnCanceled);
task.ContinueWith(t => ..., TaskContinuationOptions.OnlyOnFaulted);
Attach a single continuation on failure:
Task.Run(...).ContinueWith(t => ..., TaskContinuationOptions.NotOnRanToCompletion);
The solution you can/should use depends on the surrounding code.
You need to new the token
private CancellationTokenSource _ctSource = new CancellationTokenSource();
Why are throwing an expectation in DoWork?
Exception from one thread don't bubble up another thread that started the thread.
Cancellation in Managed Threads
If a parallel Task throws an exception it'll return execution and will have it's Exception property (as an AggregateException, you should check for its InnerException) set (and either its IsCanceled or IsFaulted property set to true). Some minimal sample code from a project of mine which escalates the exception to the main thread:
var t = new Task(Initialize);
t.Start();
while (!t.IsCompleted && !t.IsFaulted)
{
// Do other work in the main thread
}
if (t.IsFaulted)
{
if (t.Exception != null)
{
if(t.Exception.InnerException != null)
throw t.Exception.InnerException;
}
throw new InvalidAsynchronousStateException("Initialization failed for an unknown reason");
}
If you use a CancellationTokenSource it should be easy to enhance this to check for IsCanceled (instead of IsFaulted)
You can also use Task.Wait() instead of the while loop... in my project and in that precise case it seemed more appropiate to use the while loop, but you need to wait for the Task to end in one way or another.
If you use Task.Run() you can use a .ContinueWith(Task) which will have the original task passed in (where you can check for IsFaulted or IsCanceled), or have it run only on faulted execution, at your will.

is it possible to catch when any Task terminates due exception and log?

Is it possible to catch when any Task terminates due exception and log? I've added CurrentDomain_UnhandledException handling but this doesn't help.
I create tasks using Task.Factory.StartNew() as usual. When somewhere inside such task exception occurs it crashes silently (but it supposed to work forever, i'm also using LongRunning option). So I want to be notified about such behavior.
Ideallly I want to set some option somewhere to be notified when any Task crashes due exception.
If it is not possible then likely I should add something to each Task I create? Of course I can just add big try{} finally{} block inside each Task, but probably there are better solutions?
Assuming you have a Test as Task to run:
static int Test()
{
throw new Exception();
}
First Approach - Process exception in the caller's thread:
Task<int> task = new Task<int>(Test);
task.Start();
try
{
task.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine(ex);
}
Note: The exception will be of type AggregateException. All actual exceptions are available through ex.InnerExceptions property.
Second Approach - Process exception in some task's thread:
Define the ExceptionHandler this way:
static void ExceptionHandler(Task<int> task)
{
var ex = task.Exception;
Console.WriteLine(ex);
}
Usage:
Task<int> task = new Task<int>(Test);
task.ContinueWith(ExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
task.Start();
Reference: How to: Handle Exceptions Thrown by Tasks
For tasks that you create yourself, it's reasonably simple: create your own methods which call Task.Factory.StartNew(), but then also call Task.ContinueWith(loggingDelegate, TaskContinuationOptions.OnlyOnFaulted before returning the task.
The problem is that that won't add a fault handler for tasks created by other bits of infrastructure - including by async methods in C# 5. It still might be useful to you though.
You can also use TaskScheduler.UnobservedTaskException, but as per the name that will only be called for exceptions which aren't already observed by something else. (Again, that may be fine for you...)
You can use an extension method that performs an operation when an exception has ocurred.
This happens when the Task gets Faulted. So if it has another tasks to continue with, the next one can check if the previous task was faulted and Log the exception.
I usually use this methods:
//If you want to chain more tasks..
public static Task<T> Continue<T>(this Task<T> task, Action<T> action)
{
if (!task.IsFaulted)
{
task.ContinueWith((t) => action(t.Result), TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion);
}
return task;
}
public static Task OnException(this Task task, Action<Exception> onFaulted)
{
task.ContinueWith(c =>
{
var excetion = c.Exception;
onFaulted(excetion);
},
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously);
return task;
}
So you can use:
Task.Factory.StartNew(...).OnException(ex => Log(ex));
Hope it helps.
Wrap your task.Wait() in a try/catch block and catch AggregateException. Something like this -
Task<string[]> task1 = Task<string[]>.Factory.StartNew(() => GetAllFiles(path));
// Use this line to throw an exception that is not handled.
try
{
task1.Wait();
}
catch (AggregateException ae)
{
ae.Handle((x) =>
{
if (x is UnauthorizedAccessException) // This we know how to handle.
{
Console.WriteLine("You do not have permission to access all folders
in this path.");
Console.WriteLine("See your network administrator or try
another path.");
return true;
}
return false; // Let anything else stop the application.
});
}
Details can be found here - Handle exceptions thrown by Task.
You can create a OnlyOnFaulted continuation on your Task which observes the exception and logs/reports the problem.
t.ContinueWith(task =>
{
// Report and log error
}, System.Threading.CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
The above code will run the task on the UI thread because of TaskScheduler.FromCurrentSynchronizationContext(). This may be necessary if you are using winforms and need to notify the user.

Categories

Resources