100

I had a problem with catching the exception from Task.Run which was resolved by changing the code as follows. I'd like to know the difference between handling exceptions in these two ways :

In the Outside method I can't catch the exception, but in the Inside method I can.

void Outside()
{
    try
    {
        Task.Run(() =>
        {
            int z = 0;
            int x = 1 / z;
        });
    }
    catch (Exception exception)
    {
        MessageBox.Show("Outside : " + exception.Message);
    }
}

void Inside()
{
    Task.Run(() =>
    {
        try
        {
            int z = 0;
            int x = 1 / z;
        }
        catch (Exception exception)
        {
            MessageBox.Show("Inside : "+exception.Message);
        }
    });
}
9
  • 1
    Refer Exception Handling (Task Parallel Library) Commented Aug 18, 2015 at 8:18
  • 4
    That's not a duplicate if the OP is not using await... And if he's using .net 4.x he can't use await. Commented Aug 18, 2015 at 8:20
  • @MatthewWatson Are you sure? await is .NET 4.x! Commented Aug 18, 2015 at 8:36
  • @MatthewWatson In the other hand, does matter if he's using await or not? The answer is almost the same. Commented Aug 18, 2015 at 8:37
  • 1
    @MatíasFidemraizer That requires VS2012 or later though. Impossible with VS2010. I should more accurately have said that await requires C#5 Commented Aug 18, 2015 at 12:14

7 Answers 7

104

The idea of using Task.Wait will do the trick but will cause the calling thread to (as the code says) wait and therefore block until the task has finalized, which effectively makes the code synchronous instead of async.

Instead use the Task.ContinueWith option to achieve results:

Task.Run(() =>
{
   //do some work
}).ContinueWith((t) =>
{
   if (t.IsFaulted) throw t.Exception;
   if (t.IsCompleted) //optionally do some work);
});

If the task needs to continue on the UI thread, use the TaskScheduler.FromCurrentSynchronizationContext() option as parameter on continue with like so:

).ContinueWith((t) =>
{
    if (t.IsFaulted) throw t.Exception;
    if (t.IsCompleted) //optionally do some work);
}, TaskScheduler.FromCurrentSynchronizationContext());

This code will simply rethrow the aggregate exception from the task level. Off course you can also introduce some other form of exception handling here.

Sign up to request clarification or add additional context in comments.

4 Comments

For Fire and Forgot Tasks I usually use .ContinueWith(t => ... log every ((AggregateException)t.Exception).Flatten().InnerExceptions ... , TaskContinuationOptions.OnlyOnFaulted).
I don't see the point of running a task on another thread to finally wait for it to complete, your answer keeps the parallel behavior :)
I don't understand "throw t.Exception". What does this do? I tried surrounding the Task.Run with a try/catch but nothing was caught.
@thomas - i think this is used if the data it processes is not going to be displayed to the response or is not needed in other parts of current flow
70

When a task is run, any exceptions that it throws are retained and re-thrown when something waits for the task's result or for the task to complete.

Task.Run() returns a Task object that you can use to do that, so:

var task = Task.Run(...)

try
{
    task.Wait(); // Rethrows any exception(s).
    ...

For newer versions of C# you can use await instead ot Task.Wait():

try
{
    await Task.Run(...);
    ...

which is much neater.


For completeness, here's a compilable console application that demonstrates the use of await:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main()
        {
            test().Wait();
        }

        static async Task test()
        {
            try
            {
                await Task.Run(() => throwsExceptionAfterOneSecond());
            }

            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        static void throwsExceptionAfterOneSecond()
        {
            Thread.Sleep(1000); // Sleep is for illustration only. 
            throw new InvalidOperationException("Ooops");
        }
    }
}

6 Comments

Also task.Result throws an exception, if any.
Even with newer versions of C#, it isn't always possible to use await instead of Task.Wait(). From await (C# Reference): "The asynchronous method in which await is used must be modified by the async keyword."
var task = Task.Run(...) and after task.Wait() call sync, of course catched exception as all sync ops. second called never version await Task.Run(...) call async and again you cant catch exception. or I messed up... :)
@NuriYILMAZ I'm guessing you messed up. ;) I've appended a compilable example to illustrate using await to catch the exception. Note that the test.Wait() in Main() is to allow the async method to be called - but it's not catching the the exception- the await code is catching the exception..
@ErmanAkbay No, in my example throwsExceptionAfterOneSecond() is NOT intended to be async. It's supposed to be a synchronous method that takes a while to run and then throws an exception. If it was async, then we wouldn't need to use Task.Run() to run it in the first place (you would just await it), and the whole example falls to pieces. Remember, this example is intended to show how you get an exception from Task.Run(). If you made throwsExceptionAfterOneSecond() async, I feel that it would distract from what the code is trying to demonstrate.
|
11

In your Outside code you only check whether starting a task does not throw exception not task's body itself. It runs asynchronously and the code which initiated it is done then.

You can use:

void Outside()
{
    try
    {
        Task.Run(() =>
        {
            int z = 0;
            int x = 1 / z;
        }).GetAwaiter().GetResult();
    }
    catch (Exception exception)
    {
        MessageBox.Show("Outside : " + exception.Message);
    }
}

Using .GetAwaiter().GetResult() waits until task ends and passes thrown exception as they are and does not wrap them in AggregateException.

Comments

8

You can just wait, and then exceptions bubble up to the current synchronization context (see answer by Matthew Watson). Or, as Menno Jongerius mentions, you can ContinueWith to keep the code asynchronous. Note that you can do so only if an exception is thrown by using the OnlyOnFaulted continuation option:

Task.Run(()=> {
    //.... some work....
})
// We could wait now, so we any exceptions are thrown, but that 
// would make the code synchronous. Instead, we continue only if 
// the task fails.
.ContinueWith(t => {
    // This is always true since we ContinueWith OnlyOnFaulted,
    // But we add the condition anyway so resharper doesn't bark.
    if (t.Exception != null)  throw t.Exception;
}, default
     , TaskContinuationOptions.OnlyOnFaulted
     , TaskScheduler.FromCurrentSynchronizationContext());

Comments

5

When option "Just My Code" is enabled, Visual Studio in some cases will break on the line that throws the exception and display an error message that says:

Exception not handled by user code.

This error is benign. You can press F5 to continue and see the exception-handling behavior that is demonstrated in these examples. To prevent Visual Studio from breaking on the first error, just disable Just My Code checkbox under Tools > Options > Debugging > General.

1 Comment

Bingo, this was it for me. Debugger was stopping on ThrowIfCancellationRequested() instead of it being caught in the calling method.
0

For me I wanted my Task.Run to continue on after an error, letting the UI deal with the error as it has time.

My (weird?) solution is to also have a Form.Timer running. My Task.Run has it's queue (for long-running non-UI stuff), and my Form.Timer has its queue (for UI stuff).

Since this method was already working for me, it was trivial to add error handling: If the task.Run gets an error, it adds the error info to the Form.Timer queue, which displays the error dialog.

Comments

-5

Building on @MennoJongerius answer the following keeps asynchronousity and moves the exception from the .wait to the Async Completed event handler:

Public Event AsyncCompleted As AsyncCompletedEventHandler

).ContinueWith(Sub(t)
                   If t.IsFaulted Then
                       Dim evt As AsyncCompletedEventHandler = Me.AsyncCompletedEvent
                       evt?.Invoke(Me, New AsyncCompletedEventArgs(t.Exception, False, Nothing))
                   End If
               End Sub)

1 Comment

This is not C#.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.