Exception handling using tasks and Task.WaitAny - c#

Task.WaitAll method can throw an AggregateException, but Task.WaitAny method does not.
Questions:
I do not understand for what purpose the developers of the framework did this?
How to catch an exception from a task using Task.WaitAny method?
Example:
using System;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
void MyMethodForDivideByZeroException()
{
int a = 1;
a = a / 0;
}
void MyMethodForIndexOutOfRangeException()
{
int[] MyArrayForInt = new int[2] { 1, 2 };
Console.WriteLine(MyArrayForInt[2]);
}
Task MyTask22 = new Task(MyMethodForDivideByZeroException);
Task MyTask23 = new Task(MyMethodForIndexOutOfRangeException);
MyTask22.Start();
MyTask23.Start();
Task.WaitAny(MyTask22, MyTask23); //No exceptions
//Task.WaitAll(MyTask22, MyTask23); //AggregateException
Console.WriteLine(MyTask22.Status);
Console.WriteLine(MyTask23.Status);
}
}
}

WaitAny() waits for any one of the tasks to complete (successfuly or otherwise).
When one does, with an exception, the exception is not propagated (thrown).
WaitAll() waits for all of the tasks to complete.
As they both throw an exception, these are aggregated into an AggregateException.
You can check for errors like this:
var tasks = new[] { MyTask22, MyTask23 };
int taskIndex = Task.WaitAny(tasks); //No exceptions
if (taskIndex >= 0)
{
throw tasks[taskIndex].Exception;
}
More useful information here:
https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/exception-handling-task-parallel-library
https://blog.stephencleary.com/2014/10/a-tour-of-task-part-5-wait.html

The job of the Task.WaitAny method is to inform you that a task has completed, not that a task has completed successfully.
I'll answer your questions in reverse order:
How to catch an exception from a task using Task.WaitAny method?
You probably ask how to throw the exception of a completed task that has failed. The simplest way is probably just to Wait the task:
var tasks = new Task[] { MyTask22, MyTask23 };
int completedTaskIndex = Task.WaitAny(tasks);
tasks[completedTaskIndex].Wait();
I do not understand for what purpose the developers of the framework did this?
Because otherwise the method would not be able to do its intended job. You won't be able to use this method in order to be informed about the completion of a task. Instead of getting the index of the completed task, you would get a useless and expensive exception. You could catch this exception, but you would still don't know which of the tasks is the one that caused the exception.

Related

Task WhenAll exception handling

I am looking to use Task.WhenAll in a situation where some tasks may fail, but I still need the result data from the rest of the completed tasks.
According to MSDN,
If any of the supplied tasks completes in a faulted state, the
returned task will also complete in a Faulted state, where its
exceptions will contain the aggregation of the set of unwrapped
exceptions from each of the supplied tasks.
However, what it doesn't say, is whether or not Task.WhenAll will still wait for the rest of the Tasks to complete in that instance. Can anyone provide any clarification on this issue?
As the documentation says:
Creates a task that will complete when all of the Task objects in an enumerable collection have completed.
So it will wait for all tasks to complete, regardless of whether any have thrown an exception already or been cancelled. Then it will aggregate the possible cancellations and exceptions and define its state. The results of the given tasks will be in the original tasks, as well as the exceptions and cancellations.
You can easily handle your requirement by wrapping the body of all the tasks in a try catch block and use a proper data structure as the result type of your task, so that you can understand if the task was failed or not.
An example could be useful to better understand what i am trying to tell you.
public async static void Main(string[] args)
{
var task1 = GetIntegerAsync();
var task2 = GetAnotherIntegerAsync();
var result = await Task.WhenAll(new[] {task1, task2});
foreach(var res in results)
{
// show a different message depending on res.isFailed
}
}
private static async Task<(int result, bool isFailed)> GetIntegerAsync()
{
try
{
await Task.Delay(1000).ConfigureAwait(false);
return (10, false);
}
catch(Exception)
{
return (default(int), true);
}
}
private static async Task<(int result, bool isFailed)> GetAnotherIntegerAsync()
{
try
{
await Task.Delay(600).ConfigureAwait(false);
throw new Exception("Something bad happened...");
}
catch(Exception)
{
return (default(int), true);
}
}

Wait for all tasks without throwing exceptions

If I do await Task.WhenAll(tasks) it throws exception if one or more tasks failed. I would like to examine each task objects manually.
Usually it is not good idea to catch(Exception). Is it reasonable to do it in this case? Or maybe there is another way to wait for all tasks? I cannot catch AggregateException, because if there is just one it will be unwrapped.
How to do it correctly in async method?
The AwaitCompletion extention method below does what you're looking for, with a more generic API. It returns a Task, so you'll still have to await that, either via await or Task.Wait() or something else.
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
public static class TaskExtensions
{
public static Task AwaitCompletion<T>(this ICollection<Task<T>> unfinishedTasks)
{
int remainingCount = unfinishedTasks.Count;
var promise = new TaskCompletionSource<ICollection<Task<T>>>();
var finishers = unfinishedTasks.Select(x => x.ContinueWith((task, state) =>
{
int postCount = Interlocked.Decrement(ref remainingCount);
if (postCount == 0)
{
promise.SetResult(unfinishedTasks);
}
}, remainingCount));
// force evaluation
var _ = finishers.ToList();
return promise.Task;
}
/// <summary>
/// Unlike Task.Value, this doesn't wait for completion. Hence, it also doesn't
/// throw exceptions.
/// </summary>
/// <returns>Value if completed, otherwise null (or default value if a value type)</returns>
public static T GetResultOrDefault<T>(this Task<T> self)
{
if (self.IsCompletedSuccessfully)
{
return self.Result;
}
else
{
return default(T);
}
}
}
After completion of the returned Task value, all tasks in unfinishedTasks will either be completed or error'd (or still going if you use Task.Wait() with a timeout). AwaitCompletion does not throw exceptions, and awaiting it will only throw exceptions if a timeout is reached. To retrieve exceptions, look at the tasks you passed in.
Usage:
Task[] tasksToWaitFor = ...
await tasksToWaitFor.AwaitCompletion()
foreach (var task in tasksToWaitFor)
{
Console.WriteLine("result: {}", task.GetResultOrDefault());
}
The GetResultOrDefault extension method is important. If you hit Value directly, it'll throw AggregateException when the task fails, OTOH GetResultOrDefault will return null.
Swallow the exceptions momentarily with
await Task.WhenAll(tasks).ContinueWith(delegate {} )
but remember to then check all tasks individually and properly surface errors.

WaitAll vs WaitAny

I am little bit confuse for WaitAll and WaitAny. I am trying to get exception but when i do WaitAll it return exception but When use WaitAny returns nothing.And necessary is that if any of task complete work done.Is their any Replacement of WaitAny(). WaitAll and WhenAll are different becuase i dont want let all task done. Like
try
{
int i = 0;
Task t1 = Task.Factory.StartNew(() =>
{
i = 2 * 4;
});
Task<int> t2 = Task.Factory.StartNew(() =>
{
int a = 0;
int b = 100 / a;
return 0;
});
Task[] tasks = new Task[] { t1, t2 };
Task.WaitAny(tasks);
//Task.WaitAll(tasks);// Working
}
catch (AggregateException ae)
{
var message = ae.InnerException.Message;
Console.WriteLine(message);
}
Console.ReadLine();
This is called Unobserved Exception, which basically means that exception in newly created threads in Task Parallel Library (TPL) ThreadPool, is not caught by the other thread or as stated in aforementioned link:
While unobserved exceptions will still cause the
UnobservedTaskException event to be raised (not doing so would be a
breaking change), the process will not crash by default. Rather, the
exception will end up getting eaten after the event is raised,
regardless of whether an event handler observes the exception.
This means that when using WaitAny(), if one of the tasks completes without any exception, exceptions in other tasks will not be caught.
Maybe you want something like this. Note, this uses the Task-based Asynchronous Pattern.
public static Task<Task[]> WhenAllOrFirstException(params Task[] tasks)
{
var countdownEvent = new CountdownEvent(tasks.Length);
var completer = new TaskCompletionSource<Task[]>();
Action<Task> onCompletion = completed =>
{
if (completed.IsFaulted && completed.Exception != null)
{
completer.TrySetException(completed.Exception.InnerExceptions);
}
if(countdownEvent.Signal() && !completer.Task.IsCompleted)
{
completer.TrySetResult(tasks);
}
};
foreach(var task in tasks)
{
task.ContinueWith(onCompletion)
}
return completer.Task;
}
Your WaitAny implementation will throw error as well it depends what task will complete first. WaitAny does not wait till looser task (in most cases it would be t2) got completed - so it won't got exception from it.
Update
Indeed WaitAny will not return any error as descriobed here Task.WhenAny and Unobserved Exceptions

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.

Use try/catch block on called functions or task.wait()?

what's the difference between using try catch surrounding a function block that is called by some task and calling try/catch on task.wait(). If i take care of exceptions within the function, do i still need to worry about any exception that might occur from task.wait() ?
var factory = new TaskFactory();
task t1= factory.StartNew(() => funA();
t1.Wait();
void funcA()
{
try{..}
.
.
catch{..}
}
Or
var factory = new TaskFactory();
task t1= factory.StartNew(() => funA();
try
{
t1.Wait();
}
catch{....}
void funcA()
{
}
With the first block you provided you wouldn't be able to catch any of these exceptions that may occur:
ObjectDisposedException: The Task has been disposed.
ArgumentOutOfRangeException: timeout is a negative number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater than MaxValue.
AggregateException: The Task was canceled -or- an exception was thrown during the execution of the Task.
From Task.Wait() documentation on MSDN
I use to handle exceptions in tasks with continuations, easier IMO:
Task myTask= Task.Factory.StartNew( ... ).ContinueWith(myTaskFinished=>
{
if (myTaskFinished.IsCompleted)
{
// Hooray
}
if (myTaskFinished.IsFaulted)
{
foreach (Exception ex in myTaskFinished.Exception.InnerExceptions)
//Exception handle..
}
if(myTaskFinished.IsCanceled)
{
//Cancellation token?
}
});
myTask.Wait();

Categories

Resources