Error handling in Tasks - c#

I have the following code
Task load = Task.Factory.StartNew(() =>
{
Threading.Thread.Sleep(5000);
throw new Exception("bad error");
});
try{
load.Wait();
}catch(AggregateException aex){
MessageBox.Show("Error Caught!");
}
Here as you can see, I create a task and throw an exception.
The exception is then caught on the UI thread. But with this this set up, the UI will be un responsive.
What is the work around for this to make the UI responsive and catch the Exception?

There is a couple of ways to handle this.
You could use ContinueWith and check there, or you could just hook into the global task exception handler TaskScheduler.UnobservedTaskException). (details to come)
ContinueWith for exception handling only:
load.ContinueWith(previousTask =>
{
//exception message here
}, TaskContinuationOptions.OnlyOnFaulted);
or for with a normal try catch:
load.ContinueWith(previousTask =>
{
try
{
previousTask.Result
}
catch(Exception ex){//message here}
});

You'll want to add a continuation to the task, rather than using Wait or any other means of blocking on the task.
load.ContinueWith(t => MessageBox.Show(t.Exception.Message)
, TaskContinuationOptions.OnlyOnFaulted);

Related

Task class - multiple waits and exceptions

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
}

Task continue with not working upon exception

I am in a strange situation, I am using task and continue with upon faulted i am calling one method to process upon faulted or success but the function does not get fired.
Below is my code but upon expcetion it does not executes the UdpateResult method, what I am missing here.
var task = new Task.Factory.StartNew(SomeMethod());
task.ContinueWith(
t1 => Handler.UpdateResult(t1.Result, t1.Exception),
TaskContinuationOptions.ExecuteSynchronously);
try
{
task.Wait();
}
catch (AggregateException exception)
{
foreach (var innerException in exception.Flatten().InnerExceptions)
{
if (innerException is InvalidOperationException)
{
throw innerException;
}
throw new InvalidOperationException(string.Empty, innerException);
}
}
You're trying to use t1.Result within your continuation. If t1 has faulted, then accessing t1.Result will itself throw an exception.
You either need a single continuation which can handle any kind of end result, or you should attach different continuations for success and failure cases.

Task<T> and TaskContinuationOptions Clarification in C#?

I have this simple code :
var g= Task.Factory.StartNew<int> (() => 8)
.ContinueWith (ant =>{throw null;})
.ContinueWith (a =>{ Console.WriteLine("OK");},TaskContinuationOptions.NotOnFaulted);
try{
Console.WriteLine("1");
g.Wait();
Console.WriteLine("2");
}
catch (AggregateException ex)
{Console.WriteLine("catch"); }
The Output :
1
catch
System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread.
msdn :
TaskContinuationOptions.NotOnFaulted
Specifies that the continuation task should not be scheduled if its
antecedent threw an unhandled exception. This option is not valid for
multi-task continuations.
ok .
And it is ok - not showing this line cause the prev line DID throw exception.
Questions :
Do I get the AggregateException exception because I haven't inspected the Exception property ?
Must I always inspect if the antecedent throw an exception ( in each line ? ) ? ( I can't check each line ! it doesn't make any sense and very annoying)
Wasn't the try catch block should have swallow the exception ? ( I thought that all exceptions bubble up to the wait method....so ? )
Do I get the AggregateException exception because I haven't inspected
the Exception property ?
No, you get an exception, because task g cancels by TPL(because, as msdn stated, this task will not scheduled if antescendent task throws an exception).
We have 3 tasks here:
Original Task (that uses StartNew)
First Continuation Task (that throws an exception)
Second Continuation Task (that prints OK) (this is g task from your code).
The issue is that you ask TPL to start 3d task only if 2nd task will finished successfully. This means that if this condition will not met TPL will cancel your newly created task entirely.
You got unobserved task exception because you have temporary task (task 2 in my list) that you never observe. An because you never observe it faulted state it will throw in finalizer to tell you about it.
You can check this by printing task's status in catch block:
catch (AggregateException ex)
{
Console.WriteLine("catch");
// Will print: Status in catch: Canceled
Console.WriteLine("Status in catch: {0}", g.Status);
}
Must I always inspect if the antecedent throw an exception ( in each
line ? ) ? ( I can't check each line ! it doesn't make any sense and
very annoying)
Yes you should observe antecedent tasks exception to avoid this issue:
static class TaskEx
{
public static Task ObserverExceptions(this Task task)
{
task.ContinueWith(t => { var ignore = t.Exception; },
TaskContinuationOptions.OnlyOnFaulted);
return task;
}
}
And then use it as following:
var g= Task.Factory.StartNew<int> (() => 8)
.ContinueWith (ant =>{throw null;})
.ObserveExceptions()
.ContinueWith (a =>{ Console.WriteLine("OK");});
try{
Console.WriteLine("1");
g.Wait();
Console.WriteLine("2");
}
catch (AggregateException ex)
{Console.WriteLine("catch"); }
UPDATE: Added solution to last bullet
Wasn't the try catch block should have swallow the exception ? ( I
thought that all exceptions bubble up to the wait method....so ? )
We have set of extension method (called TransformWith) in our project that can solve this particular issue and gain following:
Exception would bubble up to the catch block and
We'll not crash application with TaskUnobservedException
Here the usage
var g = Task.Factory.StartNew(() => 8)
.ContinueWith(ant => { throw null; })
// Using our extension method instead of simple ContinueWith
.TransformWith(t => Console.WriteLine("OK"));
try
{
Console.WriteLine("1");
// Will fail with NullReferenceException (inside AggregateExcpetion)
g.Wait();
Console.WriteLine("2");
}
catch (AggregateException ex)
{
// ex.InnerException is a NullReferenceException
Console.WriteLine(ex.InnerException);
}
And here is a extension method:
static class TaskEx
{
public static Task TransformWith(this Task future, Action<Task> continuation)
{
var tcs = new TaskCompletionSource<object>();
future
.ContinueWith(t =>
{
if (t.IsCanceled)
{
tcs.SetCanceled();
}
else if (t.IsFaulted)
{
tcs.SetException(t.Exception.InnerExceptions);
}
else
{
try
{
continuation(future);
tcs.SetResult(null);
}
catch (Exception e)
{
tcs.SetException(e);
}
}
}, TaskContinuationOptions.ExecuteSynchronously);
return tcs.Task;
}
}
Do I get the AggregateException exception because I haven't inspected
the Exception property ?
Tasks always throw AggregateException : http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.exception.aspx
You can get the original exception using :
var myTask = Task.Factory.StartNew(() => { throw new NotImplementedException(); });
var myException = myTask.Exception.Flatten().InnerException as NotImplementedException;
Must I always inspect if the antecedent throw an exception ( in each
line ? ) ? ( I can't check each line ! it doesn't make any sense and
very annoying)
Yes it is anoying, you should create two continuations for each task to check exceptions : one that checks if there has been an exception to handle it, and another one to continue the operation if there was no exception see TaskContinuationOptions.OnlyOnFaulted and TaskContinuationOptions.OnlyOnRanToCompletion.
You should even create a third continuation to deal with cancellation if needed.
Wasn't the try catch block should have swallow the exception ? ( I
thought that all exceptions bubble up to the wait method....so ? )
No it won't, exceptions are not thrown at higher level, you should use TaskContinuationOptions.OnlyOnFaulted on the task continuation to check if there was an exception. You can get tasks exceptions at caller's level only with the async keyword not available in .net 4
Handle AggregateExceptions like this:
catch(AggregateException aex)
{
aex.Handle(ex =>
{
// Do some handling and logging
return true;
}
}

handling exception in Tpl

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.

MSDN Example of handling an exception from the TPL - Is this a race condition?

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.

Categories