I have read a lot on how to handle exceptions in TPL but don't really understand.
Lets take this example code:
var task1 = new Task(() => { throw new Exception("Throw 1"); });
var task2 = task1.ContinueWith(t => Console.WriteLine("Catch 1:{0}", t.Exception.Message),
TaskContinuationOptions.OnlyOnFaulted);
var task3 = task2.ContinueWith(t => Console.WriteLine("Continuation"));
task1.Start();
try {
task1.Wait();
}
catch (Exception ex) {
Console.WriteLine("Wait Exception: {0}", ex.Message);
}
I expected this to print
Catch 1
Continuation
But I get
Catch 1
Continuation
Wait Exception
This means that the exception is still considered unhandled when the task completes and the task finalizer will eventually tear down the application.
How do I handle the exception inside the continuation so the finalizer will not throw? At the same time I want the task to remain in the faulted state so wrapping the task in try/catch will not work.
The background is that I want to implement the async event pattern as specified here but with error handling. My complete code looks like this
public IAsyncResult Begin(AsyncCallback callback, object state, Action action) {
var task1 = new Task(action);
var task2 = task1.ContinueWith(t => HandleException(t.Exception),
TaskContinuationOptions.OnlyOnFaulted);
if (callback != null) {
var task3 = task2.ContinueWith(t => callback(t),
TaskScheduler.FromCurrentSynchronizationContext());
var task4 = task3.ContinueWith(t => HandleException(t.Exception),
TaskContinuationOptions.OnlyOnFaulted);
}
task1.Start();
return task;
}
You do your wait on the task that fails, and if you read the documentation on Task.Wait carefully you will see that wait will rethrow the exception in this case.
But if you wait on your task3 everything should work as expected.
Of course you should keep this in mind:
When you use the OnlyOnFaulted option, it is guaranteed that the
Exception property in the antecedent is not null. You can use that
property to catch the exception and see which exception caused the
task to fault. If you do not access the Exception property, the
exception will go unhandled. Also, if you attempt to access the Result
property of a task that has been canceled or has faulted, a new
exception will be raised.
(Reference here)
And finally yet another good source on How to handle exceptions thrown by tasks
I hope this helps.
Related
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.
I'm trying to catch an exception thrown from a task method using ContinueWith and OnlyOnFaulted like below. However I get an unhandled exception while I try to run this code.
I'd like the task to run to completion since I have handled the exception already. But Task.Wait() runs into AggregateException.
var taskAction = new Action(() =>
{
Thread.Sleep(1000);
Console.WriteLine("Task Waited for a sec");
throw (new Exception("throwing for example"));
});
Task t = Task.Factory.StartNew(taskAction);
t.ContinueWith(x => Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+ t.Exception), TaskContinuationOptions.OnlyOnFaulted);
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
t.Wait();
If I handle the exception in the task method like below, everything will go normal as I expect. Task Runs Into Completion, Exception gets logged and Wait succeeds also.
var taskAction = new Action(() =>
{
try
{
Thread.Sleep(1000);
Console.WriteLine("Task Waited for a sec");
throw (new Exception("throwing for example"));
}
catch (Exception ex)
{
Console.WriteLine("Catching the exception in the Action catch block only");
}
});
Task t = Task.Factory.StartNew(taskAction);
t.ContinueWith(x=> Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+ t.Exception), TaskContinuationOptions.OnlyOnFaulted);
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
t.Wait();
My question is: Am I using the OnlyOnFaulted correctly or is it always better to handle the exception in the task method itself? I would like the main thread to continue even if task runs into exception. Also, I want to log that exception from task method.
Note: I have to wait for the task method to complete before going further (with or without errors).
To summarize(my understanding so far)
If exception from Task is handled i.e. if wait or await catches the exception then the exception would be propagated to continuedtask onfaulted.
Exception can be caught even in the task method and consumed\handled.
try
{
t.wait();
}
catch(Exception e)
{
LogError(e);
}
In above case before LogError gets called, continued task associated with the main task's onfaulted gets executed.
First of all, you aren't using OnlyOnFaulted correctly. When you use ContinueWith on a task you don't really change that task, you get back a task continuation (which in your case you disregard).
If the original task faulted (i.e. had an exception thrown inside it) it would stay faulted (so calling Wait() on it would always rethrow the exception). The continuation however would run after the task faulted and handle the exception.
That means that in your code you do handle the exception, but you're also rethrowing it with Wait(). The correct code should look like this:
Task originalTask = Task.Run(() => throw new Exception());
Task continuationTask = originalTask.ContinueWith(t => Console.WriteLine(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
continuationTask.Wait()
// Both tasks completed. No exception rethrown
Now, as Yuval Itzchakov pointed out, you can handle the exception wherever you want but it would better if you were utilizing async-await to wait asynchronously if you can (you can't in Main) instead of blocking with Wait():
try
{
await originalTask;
}
catch (Exception e)
{
// handle exception
}
Am I using the TaskContinutationOptions.OnlyOnFaulted correctly or is it always better to
handle the exception in the task method itself? I would like the main
thread to continue even if task runs into exception.
You can handle exceptions either way, inside or outside. It's a matter of preference and usually depends on the use-case.
Note that one thing you aren't doing is keeping a reference to your continuation. You're using Task.Wait on the original task which propogates the exception regardless of the fact that you have a continuation which handles it.
One thing that bothers me is that you're using Task.Wait which synchronously waits instead of await which asynchronously waits. Thats the reason for the AggregationException. More so, you shouldn't block on asynchronous operations, as that will lead you down a rabit hole you probably don't want to go, with all sorts of synchronization context problems.
What i would personally do, is use await inside of ContinueWith, because its the less verbose option. Also, i'd use Task.Run over Task.Factory.StartNew:
var task = Task.Run(() =>
{
Thread.Sleep(1000);
throw new InvalidOperationException();
}
// Do more stuff here until you want to await the task.
try
{
await task;
}
catch (InvalidOperationException ioe)
{
// Log.
}
The original question happens to run its taskAction on a separate thread. That might not always be the case.
The answer by i3arnon solves the problem well. What if we do not want to use a separate thread? If we want to simply start a task, running it synchronously until we hit IO or a delay, and then continue about our own business. Only at the very end, we will wait for the task to complete.
How do we await the task with rethrowing its exception? Instead of wrapping it in Task.Run(), we will use an empty continuation that will always succeed when the task finishes for any reason.
// Local function that waits a moment before throwing
async Task ThrowInAMoment()
{
await Task.Delay(1000);
Console.WriteLine("Task waited for a sec");
throw new Exception("Throwing for example");
}
// Start the task without waiting for it to complete
var t = ThrowInAMoment();
// We reach this line as soon as ThrowInAMoment() can no longer proceed synchronously
// This is as soon as it hits its "await Delay(1000)"
// Handle exceptions in the task
t.ContinueWith(x => Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+ t.Exception), TaskContinuationOptions.OnlyOnFaulted);
// Continue about our own business
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
// Now we want to wait for the original task to finish
// But we do not care about its exceptions, as they are already being handled
// We can use ContinueWith() to get a task that will be in the completed state regardless of HOW the original task finished (RanToCompletion, Faulted, Canceled)
await t.ContinueWith(task => {});
// Could use .Wait() instead of await if you want to wait synchronously for some reason
It looks like you are on the right track. I have ran your code and get the same results. My suggestion is to use the try/catch directly from inside the action. This will make your code clearer and allow you to log things like which thread it came from, which may be different in the continuation path.
var taskAction = new Action(() =>
{
try{
Thread.Sleep(1000);
Console.WriteLine("Task Waited for a sec");
throw (new Exception("throwing for example"));
}
catch (Exception e)
{
Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+ e);
}
});
Task t = Task.Factory.StartNew(taskAction);
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
await t;
[Edit] Removed outer exception handler.
usually I encapsulated a task as follows:
Task t = Task.Factory.StartNew(() =>
{
func_can_throw_exception();
}, token).
ContinueWith
((task) =>
{
try
{
task.Wait();
}
catch (AggregateException ae)
{
ae.Handle((x) =>
{
//handle
return true;
});
}
finally
{
}
});
The questions is what happens if I wait (t.Wait();) on this task in a seperate thread (for example the GUI thread). Is this allowed. If there is an exception during task execution, how is this handled?
Don't do it like that. The better way is:
Task.Run(() => {
func_can_throw_exception();
})
.ContinueWith(task => {
do_something_with(task.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
But in the code you provided task.Wait() won't block since ContinueWith only fires after the task is finished.
In the general case, task.Wait() will block the current thread until the task is finished. If the task fails, then Wait will throw an AggregateException. But using Wait can cause deadlocks if you're not careful. It's best to use continuations in TPL code.
It's usually a bad idea to Wait on tasks since it blocks the calling thread as opposed to await which waits asynchronously. It's espically dangerous to block the GUI thread since it can very quickly lead to deadlocks.
You are handling any exception internally so unless Handle throws an exception t.Wait() would not throw any exceptions.
What you should be doing is using async-await:
try
{
await Task.Run(() => func_can_throw_exception());
}
catch (Exception e)
{
// handle exception
}
var moduleTimeOut = 3000;
var errorsThatOccurred = new List<string>();
List<string> names = new List<string>() { "Task1", "Task2", "Task3" };
var tasks = new List<Task<string>>();
names.ForEach( name => tasks.Add( Task<string>.Factory.StartNew((m) => MyTask(m.ToString()), name)));
try
{
var allTasksCompletedInTime = Task.WaitAll(tasks.ToArray(), moduleTimeOut);
}
catch (AggregateException ex)
{
foreach (var exception in ex)
{
errorsThatOccurred.Add(exception.ToString());
}
}
private string MyTask(string name)
{
if (name.Equals("Task1"))
System.Threading.Thread.Sleep(5000);
if (name.Equals("Task2"))
throw new ArgumentException("Task2 has thrown an exception");
return "MyTask has finished the execution. Task is " + name;
}
I am having some problem capturing AggregateException. Here are my different scenarios.
All tasks are completed in time. → Works fine.
Task2 throws an error and all other tasks finished in time. → Caught AggregateException and can see ArgumentException thrown by Task2.
Task1 didn't finish in time and still running. Task2 throws an error. Task3 is completed. → AggregateException didn't fire. I see the Task2 status is faulted and exception information in Exception property.
I don't know why it is not throwing AggregateException in scenario #3. Any help?
That's the expected behavior. If you hit the timeout before all the tasks have completed (either ran to completion, faulted, or cancelled), it will not throw the exception: it will just return false.
It will only throw the exception in the case where all tasks have completed within the allotted time.
I'm looking at the TPL exception handling example from MSDN #
http://msdn.microsoft.com/en-us/library/dd537614(v=VS.100).aspx
The basic form of the code is:
Task task1 = Task.Factory.StartNew(() => { throw new IndexOutOfRangeException(); });
try
{
task1.Wait();
}
catch (AggregateException ae)
{
throw ae.Flatten();
}
My question is: Is this a race condition? What happens if task1 throws before the try has executed? Am I missing something that stops this being a race?
Shouldn't it be written like this instead:
try
{
Task task1 = Task.Factory.StartNew(() => { throw new IndexOutOfRangeException(); });
task1.Wait();
}
catch (AggregateException ae)
{
throw ae.Flatten();
}
No, the first example is perfectly valid.
When the exception is raised in the Task it is wrapped in an AggregateException. Only when another thread joins the task, in this example by calling task1.Wait() is the exception propogated to the joining thread. Essentially the exception is 'stored' until it can be propogated back to a thread that is waiting for the feedback.