Task and exception silence

前端 未结 4 637
暗喜
暗喜 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);
    }
    
    0 讨论(0)
  • 2021-02-04 04:25

    The behaviour of that scenario depends on what framework you have; in 4.0, you actually need to be careful - if you don't handle TaskScheduler.UnobservedTaskException, it will error later when it gets collected/finalized, and will kill your process.

    TaskScheduler.UnobservedTaskException += (sender, args) =>
    {
        Trace.WriteLine(args.Exception.Message); // somebody forgot to check!
        args.SetObserved();
    };
    

    This changes in 4.5, IIRC.

    To check the outcome of a Task that might fail, you could register a continuation - i.e. call ContinueWith and check the result's exception. Alternatively, accessing the .Result of the task (which would also do an implicit Wait()) will re-surface the exception that happened. It is good to observe the result of a task, as that clears the finalization flag, meaning it can be collected more cheaply.

    0 讨论(0)
  • 2021-02-04 04:27

    No, tasks are not threads. Tasks represent a high level abstraction - they are a unit of work that is inherently parallelisable. Threads run units of work.

    In your first example, you create a unit of work and then tell it to run itself (how it does so is an implementation detail of Task). Whereas in your second example you explicitly schedule a unit of work (it would appear in a different manner to the implementation of Task).

    0 讨论(0)
  • 2021-02-04 04:30

    I think you are not getting exception in case of Task because you are not waiting for exception in the main thread. You are just continuing. Put task.Wait() and you will get the exception in main thread.

    0 讨论(0)
提交回复
热议问题