The following code creates a task that is canceled. await expression (case 1) throws a System.OperationCanceledException , while synchronous Wait() (case 2) throws a System.Threading.Tasks.TaskCanceledException (wrapped in System.AggregateException ).
using System; using System.Threading; using System.Threading.Tasks; public class Program { public static void Main() { Program.MainAsync().Wait(); } private static async Task MainAsync() { using(var cancellationTokenSource = new CancellationTokenSource()) { var token = cancellationTokenSource.Token; const int cancelationCheckTimeout = 100; var task = Task.Run( async () => { for (var i = 0; i < 100; i++) { token.ThrowIfCancellationRequested(); Console.Write("."); await Task.Delay(cancelationCheckTimeout); } }, cancellationTokenSource.Token ); var cancelationDelay = 10 * cancelationCheckTimeout; cancellationTokenSource.CancelAfter(cancelationDelay); try { await task;
The output of the 1st case:
..........System.OperationCanceledException: The operation was canceled. at System.Threading.CancellationToken.ThrowIfCancellationRequested() at Program.<>c__DisplayClass1_0.<<MainAsync>b__0>d.MoveNext() --- 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 Program.<MainAsync>d__1.MoveNext() Task.IsCanceled: True Task.IsFaulted: False Task.Exception: null
Result 2:
..........System.AggregateException: One or more errors occurred. ---> System.Threading.Tasks.TaskCanceledException: A task was canceled. --- End of inner exception stack trace --- at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.Wait() at Program.<MainAsync>d__1.MoveNext() ---> (Inner Exception #0) System.Threading.Tasks.TaskCanceledException: A task was canceled.<--- Task.IsCanceled: True Task.IsFaulted: False Task.Exception: null
Why does System.AggregateException in the second case not contain a System.OperationCanceledException as an internal exception?
I know that ThrowIfCancellationRequested() throws an OperationCanceledException , and we see that in both cases the Task goes into the cancel state (not erroneous).
This puzzles me because canceling a method from the .NET API provides consistent behavior in both cases - a canceled task throws only a TaskCanceledException :
using System; using System.Threading; using System.Threading.Tasks; public class Program { public static void Main() { Program.MainAsync().Wait(); } private static async Task MainAsync() { using(var cancellationTokenSource = new CancellationTokenSource()) { var token = cancellationTokenSource.Token; var task = Task.Delay(1000, token); cancellationTokenSource.CancelAfter(100); try { await task;
The output of the 1st case:
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 Program.<MainAsync>d__1.MoveNext() Task.IsCanceled: True Task.IsFaulted: False Task.Exception: null
Result 2:
System.AggregateException: One or more errors occurred. ---> System.Threading.Tasks.TaskCanceledException: A task was canceled. --- End of inner exception stack trace --- at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.Wait() at Program.<MainAsync>d__1.MoveNext() ---> (Inner Exception #0) System.Threading.Tasks.TaskCanceledException: A task was canceled.<--- Task.IsCanceled: True Task.IsFaulted: False Task.Exception: null