Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TPL and Exception Handling

All, there are many question on the above topic but I believe this is sufficiently different to warrant a new question. I have the following Task and a continuation to deal with a variety of task Status; TaskStatus.RanToCompletion, TaskStatus.Canceled and of course the AggregateException via TaskStatus.Faulted. The code looks like

Task<bool> asyncTask = Task.Factory.StartNew<bool>(() =>
    asyncMethod(uiScheduler, token, someBoolean), token);

asyncTask.ContinueWith(task =>
{
    // Check task status.
    switch (task.Status)
    {
        // Handle any exceptions to prevent UnobservedTaskException.             
        case TaskStatus.RanToCompletion:
            if (asyncTask.Result)
            {
                // Do stuff...
            }
            break;
        case TaskStatus.Faulted:
            if (task.Exception != null)
                mainForm.progressRightLabelText = task.Exception.InnerException.Message;
            else
                mainForm.progressRightLabelText = "Operation failed!";
        default:
            break;
    }
}

This all works well, but I am concerned whether or not I am doing this right, as there is the possibility of an AggregateException being thrown from within the continuation - what then?

I do not want to Wait on my asyncTask nor the continuation as this will block the return to the UI Thread. To catch any exceptions thrown from within a continuation can't mean I have to do something like this surely

Task parentTask = Task.Factory.startNew(() => 
    {
        Task<bool> asyncTask = Task.Factory.StartNew<bool>(() =>
            asyncMethod(uiScheduler, token, someBoolean), token);

        Task continueTask = asyncTask.ContinueWith(task =>
            {
                // My continuation stuff...   
            }

        try
        {
            continueTask.Wait();
        }
        catch(AggregateException aggEx)
        { 
            // Some handling here...
        }
    });

would this even work? What is best practice here?

As always, thanks for your time.

like image 491
MoonKnight Avatar asked Mar 27 '12 09:03

MoonKnight


2 Answers

You can use traditional try/catch within your delegates watching for AggregateException or you can chain on specific continuations that will only ever run if the antecedent has faulted using the TaskContinuationOptions.OnlyOnFaulted option. The latter approach allows for very clean task workflows to be defined. For example:

Task myRootTask = ....;

myRootTask.ContinueWith(rootAntecdent =>
{
    // this will only be executed if the antecedent completed successfully, no need to check for faults
},
TaskContinuationOptions.OnlyOnRanToCompletion);

myRootTask.ContinueWith(rootAntecedent =>
{
    // this will only be executed if the antecedent faulted, observe exception and handle accordingly
},
TaskContinuationOptions.OnlyOnFaulted);
like image 178
Drew Marsh Avatar answered Oct 02 '22 20:10

Drew Marsh


Msdn has a fairly well written "How to" on the subject: here

You will notice they simply use a try/catch(AggregateException) block, then filter the exception they know how to handle in ae.Handle(lambda) and make the app stop if there are some left that aren't handleable.

like image 23
Louis Kottmann Avatar answered Oct 02 '22 19:10

Louis Kottmann