【发布时间】:2016-03-25 08:52:46
【问题描述】:
以下代码创建了一个正在取消的任务。 await 表达式(案例 1)抛出 System.OperationCanceledException,而同步 Wait()(案例 2)抛出 System.Threading.Tasks.TaskCanceledException(包裹在 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; // (1)
//task.Wait(); // (2)
}
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())}");
}
}
}
}
案例 1 输出:
..........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
案例 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
为什么第二种情况下的System.AggregateException 不包含System.OperationCanceledException 作为内部异常?
我知道ThrowIfCancellationRequested() 会抛出OperationCanceledException,我们可以看到在这两种情况下Task 都会进入取消(非故障)状态。
这让我很困惑,因为从 .NET API 中取消方法会在两种情况下产生一致的行为 - 取消的任务只会抛出 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; // (1)
//task.Wait(); // (2)
}
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())}");
}
}
}
}
案例 1 输出:
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
案例 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
【问题讨论】:
-
这有关系吗?
TaskCanceledException是OperationCanceledException的派生类,所以如果你做了catch(OperationCanceledException e),你会捕获这两种类型的异常。您丢失的唯一信息是TaskCanceledException.Task属性。 -
我同意,但我更好奇在已经有
OperationCanceledException的情况下引入/使用TaskCanceledException的原因。 -
引入它的原因是,当您在某些情况下能够获取已取消的任务的上下文时,因此在您可以抛出包含任务的更派生形式的情况下。
ThrowIfCancellationRequested是一般写的,它不知道它在 Task 内部,因此它会在没有 Task 属性的情况下引发更一般的异常。
标签: c# .net exception async-await task-parallel-library