How to handle Task.Run Exception

旧时模样 提交于 2019-12-28 12:15:30

问题


I had a problem with catching my exception from Task.Run I changed my code and my problem solved. I'm willing to figure out what is the difference between handling exceptions inside Task.Run in these two ways :

In Outside function I can't catch the exception but in Inside I can catch it.

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);
        }
    });
}

回答1:


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");
        }
    }
}



回答2:


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.




回答3:


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.




回答4:


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());



回答5:


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.



来源:https://stackoverflow.com/questions/32067034/how-to-handle-task-run-exception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!