2
votes

I would like to run something like this and wait on it, as a whole:

            Task task1 = Task.Factory.StartNew(() => MethodThatCouldThrow());

            Task task2 = task1.ContinueWith(t => HandleFailure(t.Exception),
                TaskContinuationOptions.OnlyOnFaulted);

Note that there are a few discussions on the topic already (links below), but they do not quite answer this:

  1. How can one wait on one thing to ensure work is done(*work is defined as main task plus possible continuation)
  2. How can one avoid exception handling for normal cases. The normal cases are:

    • task1 completes (RanToCompletion), and task2 does not run (Canceled)
    • task1 faults (Faulted), but task2 successfully handles it (RanToCompletion) (other cases are not normal and should throw, for example task2 faults)

Why can't I wait just on task2? Because it's "Canceled" if task1 succeeds.

Why can't I wait just on task1? Because it's "Faulted" if it failed (and task2 may still be running).

Why can't I wait on both tasks? Because task2 is "Canceled" if task1 succeeds, and this wait throws.

Couple of possible workarounds (however they do not meet desired conditions):

  • unconditional continuation, which checks what to do
  • conditional continuation but more complex wait, for example, wait on task2 and ignore cancelation exception if task1 succeeded.

Relevant threads I could find:

Waiting on a Task with a OnlyOnFaulted Continuation causes an AggregateException

Using Tasks with conditional continuations

Thanks!

2
You can wait (or await) on everything you want to. Just do it in a try catch blocki3arnon

2 Answers

1
votes

You should probably first wait until both tasks are completed. Then examine the tasks using any predicate you like and decide based on that.

You can wait for the two tasks either by simply ignoring any exceptions that the wait throws (which I think is dirty since it is exception-based control flow), or:

var combinedTask = Task.WhenAll(t1, t2);
var combinedTaskNoError = waitTask.ContinueWith(_ => { }, TCO.ExecuteSynchronously);

await combinedTaskNoError; //Will not throw.

That middle line should probably be encapsulated in a helper method. I'd call it WhenCompleted.

1
votes

For your first point using Task.WhenAll(task1,task2).Wait() should suffice.

As for your second point, you can ignore task 2 being cancelled like so

combinedTask.ContinueWith(_=>{
    if (_.IsFaulted){
        if (typeof(_.Exception) == typeof(TaskCancelledException){
            // Do Nothing
        }
    }
})

If you know the exception type of task1's failure, you could handle this the same way. Depending on how you chain the tasks together, it could only ever produce AggregateExceptions, which would mean that you would need to be doing you Exception type check on InnnerExceptions until you reach an exception that is not of type AggregateException