Why exceptions thrown within a task are silent exception and you never know if a certain exception has been thrown
try
{
Task task = new Task(
() => {
The following assumes .NET 4.0, and the using the default TaskScheduler.
First of all notice that the exceptions are raised inside the delegates you pass, so they are raised in a different thread, not in the one you (logically) are doing your catch
. So somehow the exception must be propagated from the thread that executes your delegate / lambda code to the one that started the thread/task.
Note that for Task
, I think that the library can also choose not to run it on it's own thread, but rather on the calling thread (but I'm not sure if that was only true for Parallel.ForEach
, etc. and not for a "naked" Task
object).
For that two happen, two things must be fulfilled:
Having that said, both of your examples don't wait for the thread/task to finish. In the first example you're missing a task.Wait()
(or similar) and in the second a thread.Join()
. Depending on your test codes timing behavior this may mean that you may never be able to observe the exception from the thread/task (item 1 above).
Even if you add the two calls this is what happens for me (again .NET 4.0):
Task example: the call to task.Wait()
actually reraises the exception originally left unhandled in the task's delegate (this is what the TPL will do for you internally), it does wrap it inside a System.AggregateException
, which you could/would see if you'd use something more precise then a flat "catch-all".
Thread example: the exception raised by the delegate remains unhandled and your application exits (unless you do anything to deal with unhandled exceptions differently)
In other words I would have the examples as follows:
// Thread example
var thread = new Thread(() => { throw null; });
thread.Start();
thread.Join();
// Should never reach here, depending on timing sometimes not even
// until the Join() call. The process terminates as soon as the other
// thread runs the "throw null" code - which can logically happen somewhere
// after the "start" of the "Start()" call.
// Task example
try
{
var task = new Task(() => { throw null; });
task.Start();
task.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine("Exception: " + ex);
}