Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way of handling exception in task continuewith

Please have a look at the following code-

static void Main(string[] args) {     // Get the task.     var task = Task.Factory.StartNew<int>(() => { return div(32, 0); });      // For error handling.     task.ContinueWith(t => { Console.WriteLine(t.Exception.Message); },          TaskContinuationOptions.OnlyOnFaulted);      // If it succeeded.     task.ContinueWith(t => { Console.WriteLine(t.Result); },          TaskContinuationOptions.OnlyOnRanToCompletion);     Console.ReadKey();     Console.WriteLine("Hello"); }  private static int div(int x, int y) {     if (y == 0)     {         throw new ArgumentException("y");     }     return x / y; } 

If I execute the code in release mode, The output is "One or more errors occurred" and once I hit the "Enter key, "Hello" is also getting displayed. If I run the code in debug mode, the output is same as release mode. But when debugging in IDE, A IDE exception message ("Unhandled exception in user code" ) appears when the control executes the line

throw new ArgumentException("y");  

If I continue from there on, the program does not crash and displays the same output as release mode. Is this proper way to handle exception?

like image 324
Anirban Paul Avatar asked Feb 03 '14 06:02

Anirban Paul


People also ask

How do you handle exceptions in tasks?

Exceptions are propagated when you use one of the static or instance Task. Wait methods, and you handle them by enclosing the call in a try / catch statement. If a task is the parent of attached child tasks, or if you are waiting on multiple tasks, multiple exceptions could be thrown.

What happens if a task throws an exception?

If Bar throws an exception, it will be thrown right at the point where you call it. However, if the Task that Bar returns wraps an exception, what happens depends on your version of . NET runtime - for .

What does calling task ContinueWith () do?

ContinueWith(Action<Task>)Creates a continuation that executes asynchronously when the target Task completes.

When should I use task ContinueWith?

The ContinueWith function is a method available on the task that allows executing code after the task has finished execution. In simple words it allows continuation. Things to note here is that ContinueWith also returns one Task. That means you can attach ContinueWith one task returned by this method.


2 Answers

You probably don't need separate OnlyOnFaulted and OnlyOnRanToCompletion handlers, and you're not handling OnlyOnCanceled. Check this answer for more details.

But when debugging in IDE, A IDE exception message ("Unhandled exception in user code" ) appears when the control executes the line

You see the exception under debugger because you probably have enabled it in Debug/Exceptions options (Ctrl+Alt+E).

If I continue from there on, the program does not crash and displays the same output as release mode. Is this proper way to handle exception?

An exception which was thrown but not handled inside a Task action will not be automatically re-thrown. Instead, it be wrapped for future observation as Task.Exception (of type AggregateException). You can access the original exception as Exception.InnerException:

Exception ex = task.Exception; if (ex != null && ex.InnerException != null)     ex = ex.InnerException; 

To make the program crash in this case, you actually need to observe the exception outside the task action, e.g. by referencing the Task.Result:

static void Main(string[] args) {     // Get the task.     var task = Task.Factory.StartNew<int>(() => { return div(32, 0); });      // For error handling.     task.ContinueWith(t => { Console.WriteLine(t.Exception.Message); },          TaskContinuationOptions.OnlyOnFaulted);      // If it succeeded.     task.ContinueWith(t => { Console.WriteLine(t.Result); },          TaskContinuationOptions.OnlyOnRanToCompletion);      Console.ReadKey();      Console.WriteLine("result: " + task.Result); // will crash here      // you can also check task.Exception      Console.WriteLine("Hello"); } 

More details: Tasks and Unhandled Exceptions, Task Exception Handling in .NET 4.5.

Updated to address the comment: here is how I would do this in a UI app with .NET 4.0 and VS2010:

void Button_Click(object sender, EventArgs e) {     Task.Factory.StartNew<int>(() =>      {         return div(32, 0);      }).ContinueWith((t) =>     {         if (t.IsFaulted)         {             // faulted with exception             Exception ex = t.Exception;             while (ex is AggregateException && ex.InnerException != null)                 ex = ex.InnerException;             MessageBox.Show("Error: " + ex.Message);         }         else if (t.IsCanceled)         {             // this should not happen              // as you don't pass a CancellationToken into your task             MessageBox.Show("Canclled.");         }         else         {             // completed successfully             MessageBox.Show("Result: " + t.Result);         }     }, TaskScheduler.FromCurrentSynchronizationContext()); } 

For as long as you target .NET 4.0 and you want the .NET 4.0 behavior for unobserved exceptions (i.e., re-throw when task gets garbage-collected), you should explicitly configure it in the app.config:

<?xml version="1.0" encoding="utf-8"?> <configuration>   <startup>     <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>   </startup>   <runtime>     <ThrowUnobservedTaskExceptions enabled="true"/>   </runtime> </configuration> 

Check this for more details:

Unobserved task exceptions in .NET4

like image 62
noseratio Avatar answered Sep 21 '22 16:09

noseratio


What you have there is an AggregateException. This is thrown from tasks and requires you to check the inner exceptions to find specific ones. Like this:

task.ContinueWith(t => {     if (t.Exception is AggregateException) // is it an AggregateException?     {         var ae = t.Exception as AggregateException;          foreach (var e in ae.InnerExceptions) // loop them and print their messages         {             Console.WriteLine(e.Message); // output is "y" .. because that's what you threw         }     } }, TaskContinuationOptions.OnlyOnFaulted); 
like image 44
Simon Whitehead Avatar answered Sep 20 '22 16:09

Simon Whitehead