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);
}
});
}
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.
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 .
To avoid having to iterate over nested AggregateException exceptions, you can use the Flatten method to remove all the nested AggregateException exceptions, so that the InnerExceptions property of the returned AggregateException object contains the original exceptions.
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.
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");
}
}
}
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.
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
.
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());
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