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 theException
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:
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:
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 ? )
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With