In Steven Toub's article:
To make it easier for developers to write asynchronous code based on Tasks, .NET 4.5 changes the default exception behavior for unobserved exceptions. While unobserved exceptions will still cause the UnobservedTaskException event to be raised (not doing so would be a breaking change), the process will not crash by default. Rather, the exception will end up getting eaten after the event is raised, regardless of whether an event handler observes the exception.
But the result of my experiment does not match the above statement. Below is my code:
static void Main(string[] args)
{
DownloadAsync("http://an.invalid.url.com);
}
async static void DownloadAsync(string url)
{
using (var client = new System.Net.Http.HttpClient())
{
string text = await client.GetStringAsync(url);
Console.WriteLine("Downloaded {0} chars", text.Length);
}
}
Since I pass an invalid url to DownloadAsync()
method, the call to HttpClient
's GetStringAsync()
method will throw an expcetion, and it crashes the application.
So my question is: Does unobserved exceptions in .NET 4.5 still crash app by default?
An “unobserved” exception is one that's stored into the task but then never looked at in any way by the consuming code. There are many ways of observing the exception, including Wait()'ing on the Task, accessing a Task<TResult>'s Result, looking at the Task's Exception property, and so on.
You can also handle the original exceptions by using the AggregateException. Handle method. Even if only one exception is thrown, it is still wrapped in an AggregateException exception, as the following example shows. public static partial class Program { public static void HandleThree() { var task = Task.
When an exception occurs in an async method that has a return type of Task or Task<TResult>, the exception object is wrapped in an instance of AggregateException and attached to the Task object. If multiple exceptions are thrown, all of them are stored in the Task object.
You do have a Task
with an exception (the one returned by GetStringAsync
). However, the await
is observing the Task
exception, which then propagates out of the DownloadAsync
method (which is async void
).
Exceptions propagating out of async void
methods behave differently; they are raised on the SynchronizationContext
that was active when the async void
method started (in this case, a thread pool SynchronizationContext
). This is not considered an unobserved exception.
If you change DownloadAsync
to return Task
, then you will have an actual unobserved Task
exception, which will be ignored (correctly):
static void Main(string[] args)
{
DownloadAsync("http://an.invalid.url.com);
Console.ReadKey();
}
async static Task DownloadAsync(string url)
{
using (var client = new System.Net.Http.HttpClient())
{
string text = await client.GetStringAsync(url);
Console.WriteLine("Downloaded {0} chars", text.Length);
}
}
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