Mark task exception as handled

1.9k views Asked by At

How can I mark an exception thrown in a task as handled. The problem is when I call the Wait() method of a task, it will throw an AggregateException even when I have already handled the AggregateException a long time ago. The following code snippet shows the problem I want to solve. I my original code I handle the AggregateException in one part of my code and I call the Wait() method in another part of my code. But the problem is the same.

static void Main(string[] args)
{
    Task task = null;

    try
    {
        task = new Task(() =>
        {
            Console.WriteLine("Task started");
            Thread.Sleep(1000);
            throw new InvalidOperationException("my test exception");
        });

        task.ContinueWith(t => 
        {
            Console.WriteLine("Task faulted");
            AggregateException ae = t.Exception;
            ae.Flatten().Handle(ex =>
            {
                if (typeof(InvalidOperationException) == ex.GetType())
                {
                    Console.WriteLine("InvalidOperationException handled --> " + ex.Message);
                    return true;
                }

                return false;
            });
        }, TaskContinuationOptions.OnlyOnFaulted);

        task.Start();
        Thread.Sleep(2000);
        task.Wait();
    }
    catch (AggregateException ae)
    {
        Console.WriteLine("AggregateException thrown again!!! Why???");
        ae.Flatten().Handle(ex =>
            {
                Console.WriteLine(ex.Message);
                return true;
            });              
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }

    Console.WriteLine("Finished");
    Console.Read();
}

The code above produces the following output:

  • Task started
  • Task faulted
  • InvalidOperationException handled --> my test exception
  • AggregateException thrown again!!! Why???
  • my test exception
  • Finished
1

There are 1 answers

0
i3arnon On BEST ANSWER

When a faulted task is Waited the exception is rethrown. It would be unreliable design if an exception would be thrown just sometimes.

But, if you're adding a continuation that handles the exception and you don't want it thrown again then simply don't Wait that task again. Wait the continuation task (that you're currently not using) instead. It would only complete after the original task completed and if you need the result simply have the continuation return that. This way the exception would be handled only once:

Task continuation = task.ContinueWith(t => 
{
    Console.WriteLine("Task faulted");
    AggregateException ae = t.Exception;
    ae.Flatten().Handle(ex =>
    {
        if (typeof(InvalidOperationException) == ex.GetType())
        {
            Console.WriteLine("InvalidOperationException handled --> " + ex.Message);
            return true;
        }

        return false;
    });
}, TaskContinuationOptions.OnlyOnFaulted);

task.Start();
Thread.Sleep(2000);
continuation.Wait();

Note: That will throw a TaskCanceledException when the original task doesn't throw an exception because the continuation is canceled (due to TaskContinuationOptions.OnlyOnFaulted). To avoid that simply remove the flag and check whether t.IsFaulted.