1

In a WPF app, I added two buttons, one which starts a Task and the other which forces GC to run:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Task.Run(() =>
        {
            throw new Exception("Some Exception");
        });
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

The task just throws an exception.

When running the app in Release config, I'm pressing first button and then the 2nd button and I'm expecting the app to crash. This doesn't happen, no matter how many times I press the buttons.

I am aware of the behavior of unobserved task swallowing exceptions in .NET 4.5: https://blogs.msdn.microsoft.com/pfxteam/2011/09/28/task-exception-handling-in-net-4-5/

I am confused however why once GC runs, the app doesn't crash. Per my understanding, Once the task is collected the eception should be thrown.

Also note I am aware of this question which doesn't answer my question Why doesn't my process terminate when Task has unhandled exception?

The comment marked as answer suggests to use ContinueWith but this doesn't work either, the app doesn't crash, the exception is swallowed:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Task.Run(() =>
        {
            throw new Exception("Some Exception");
        }).ContinueWith(t =>
        {
            if (t.IsFaulted) { throw t.Exception.InnerException; }
        });
    }

I am already aware that awaiting on the Task makes the app to crash.

Don Box
  • 3,166
  • 3
  • 26
  • 55
  • What is your framework, did you set ThrowUnobservedTaskExceptions ? – bommelding Jun 26 '18 at 11:38
  • You aren't using `async/await` anywhere. You are firing a Task and never check if it succeeded or failed. If you did, ie if you wrote `await Task.Run(()=>throw new Exception(..));` the exception would be rethrown after the `await` – Panagiotis Kanavos Jun 26 '18 at 11:51
  • 1
    If you add the `` element to your config file and press the first button and then the second button, the process should crash. This is the expected behaviour. See my answer. – mm8 Jun 26 '18 at 11:51
  • BTW the app doesn't crash because it's not supposed to. Unobserved Task exceptions do *not* crash the application – Panagiotis Kanavos Jun 26 '18 at 11:53
  • First, *don't* change the default behaviour or you may break libraries that depend on it. Second, check the documentation of the [TaskScheduler.UnobservedTaskException](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskscheduler.unobservedtaskexception?view=netframework-4.7.2) event. It explains that `the process does not terminate by default` when an unobserver task exception occurs. – Panagiotis Kanavos Jun 26 '18 at 12:06

2 Answers2

5

I am confused however why once GC runs, the app doesn't crash. Per my understanding, Once the task is collected the eception should be thrown.

Your understanding is correct provided that you use the old behaviour (pre .NET Framework 4.5) by adding the following element to your App.config:

<runtime>
    <ThrowUnobservedTaskExceptions enabled="true"/>
</runtime>

An UnobservedTaskException will not cause the process to crash if you don't do this. The exception is still raised though so you could handle the TaskScheduler.UnobservedTaskException event in both cases.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • OK, so how to handle `TaskScheduler.UnobservedTaskException` without setting `ThrowUnobservedTaskExceptions enabled="true"`? – Don Box Jun 26 '18 at 11:52
  • 1
    Like you would handle any other event, i.e. hook up an event handler to it: `TaskScheduler.UnobservedTaskException += (ss, ee) => { /* do something */ };` – mm8 Jun 26 '18 at 11:53
  • Thanks!!!!!!!!!! – Don Box Jun 26 '18 at 12:35
4

This is because you fire and forget. The exception will happen in a separate thread. If you want it to crash you'll have to await it or make it sync.

private void Button_Click(object sender, RoutedEventArgs e)
{
    await Task.Run(() =>
    {
        throw new Exception("Some Exception");
    });
}
Ray Krungkaew
  • 6,652
  • 1
  • 17
  • 28
  • I am aware the exception happens on another thread. Why doesn't the app crashes? If you create a thread with `new Thread()` and throw an exception on the thread's method, the app crashes. So why doesn't it crash when using Task.Run too? – Don Box Jun 26 '18 at 11:14
  • Note that I mentioned I am also aware of 'unobserved tasks'. I know that if I call `await` it will work. That's not what I am asking. – Don Box Jun 26 '18 at 11:15
  • The exception happens on another _Task_ , not on a bare Thread. Tasks catch exceptions, Threads kill the AppDomain – bommelding Jun 26 '18 at 11:31
  • @bommelding Did you read my question? I am already aware of that. – Don Box Jun 26 '18 at 11:33
  • @DonBox what is the actual question? Unobserved exceptions aren't supposed to crash the application. The solution is to add proper exception handling and actually *observe* the tasks. Handling the [UnobservedTaskException](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskscheduler.unobservedtaskexception?view=netframework-4.7.2) event is only meant as a last resort. – Panagiotis Kanavos Jun 26 '18 at 12:03
  • @DonBox did you use `async void` perhaps and wonder how to handle exceptions thrown from such a method? Don't use `async void` except for event handlers. An asynchronous method that doesn't return results should use `async Task` – Panagiotis Kanavos Jun 26 '18 at 12:04
  • You have a truck containing the latest model of Tesla self-driving car. You set the car to self-drive to a wall. You put a garbage collector into a car. The car run to the wall. The question is why the truck does not crash. – Ray Krungkaew Jun 26 '18 at 15:20