Task Cancelled Exception (ThrowForNonSuccess)

2019-03-20 16:42发布

问题:

This is a continuation from this question: Multiple Task Continuation

I have changed my code as in the answer, however now I am receiving TaskCancelledExceptions when I try to run tasks.

public virtual async Task RunAsync(TaskWithProgress task)
{
    Show();
    TaskIsRunning();
    await SetCompletedHandler(TaskComplete());
    await SetCancelledHandler(TaskCancelled())
    await SetFaultedHandler(TaskFaulted());
    await task;
    Close();
}

however the following code does not. I am a bit stuck as to why.

public virtual Task RunAsync(TaskWithProgress task)
{
    Show();
    TaskIsRunning();
    SetCompletedHandler(TaskComplete());
    SetCancelledHandler(TaskCancelled())
    SetFaultedHandler(TaskFaulted());
    return task;
}

The calling code basically involves the following:

await progressDialog.RunAsync(task);

Edit:

I do not cancel a cancellationtoken anywhere so I can't see why this is throwing that exception.

The three SetXXXHandler() methods basically perform the following code with different continuation status:

task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled, this.Scheduler);

The Stack trace is here:

   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at FugroDXExt.frmBaseProgressAsync.<RunAsync>d__7.MoveNext() in d:\C#\FugroDXExt\trunk\frmBaseProgressAsync.cs:line 92
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at FCP.Forms.frmProcessing.<mnuApplyCenteredSmoothing_ItemClick>d__34.MoveNext() in d:\C#\FCP\FCP\Forms\frmProcessing.cs:line 578

Close() simply closes the form. If I remove that line the same thing occurs.

回答1:

You say that SetCancelledHandler just adds a continuation to the task. I assume that's the same task RunAsync gets as a parameter although i can't tell by your code how SetCancelledHandler gets a task to continue on (I assume we're missing some code). Anyways...

You register 3 continuations on a task that will run when the task completes, is canceled and is faulted. Now let's assume the original task ran to completion successfully without being canceled. That means that 2 of your continuations (OnCanceled and OnFaulted) will not run because they don't need to be. The way to tell a task to not run in the TPL is to cancel it, and that happens automatically.

The difference between your 2 code snippets is that in the first one you await the task continuations, and they get canceled which explains your exception. On the second snippet you don't await the continuations, just the original task that successfully ran to completion.

P.S: I think the second option is more appropriate. You don't need to await all those continuations. You want to let them run if they need to.

TL;DR: You await a canceled continuation task. The continuation task, not the original, is the one that throws an exception.