Task and exception silence

前端 未结 4 769
暗喜
暗喜 2021-02-04 04:04

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(
  () => {
            


        
4条回答
  •  甜味超标
    2021-02-04 04:09

    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:

    1. The calling thread is still active. Otherwise there is nothing left that could actually perform the catch.
    2. The exception that was caused in the thread/task must somehow be preserved and reraised for you to catch it.

    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);
    }
    

提交回复
热议问题