OperationCanceledException VS TaskCanceledException when task is canceled
The difference here comes from using token.ThrowIfCancellationRequested()
. This method checks for cancellation and if requested throws OperationCanceledException
specifically and not TaskCanceledException
(understandable as CancellationToken
isn't exclusive to the TPL). You can look at the reference source and see that it calls this method:
private void ThrowOperationCanceledException()
{
throw new OperationCanceledException(Environment.GetResourceString("OperationCanceled"), this);
}
"Regular" cancellation though will indeed generate a TaskCanceledException
. You can see that by cancelling the token before the task had a chance to start running:
cancellationTokenSource.Cancel();
var task = Task.Run(() => { }, cancellationTokenSource.Token);
try
{
await task;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Console.WriteLine($"Task.IsCanceled: {task.IsCanceled}");
Console.WriteLine($"Task.IsFaulted: {task.IsFaulted}");
Console.WriteLine($"Task.Exception: {((task.Exception == null) ? "null" : task.Exception.ToString())}");
}
Output:
System.Threading.Tasks.TaskCanceledException: A task was canceled.
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Sandbox.Program.<MainAsync>d__1.MoveNext()
Task.IsCanceled: True
Task.IsFaulted: False
Task.Exception: null
Traditional .Net methods usually don't use CancellationToken.ThrowIfCancellationRequested
for async API as this is only appropriate when offloading work to another thread. These methods are for inherently asynchronous operations so cancellation is monitored using CancellationToken.Register
(or the internal InternalRegisterWithoutEC
).
TaskCanceledException
inherits from OperationCanceledException
. So it least there is a little consitency.
if( ex is OperationCanceledException)
{
...
}