Task.Run(Action, CancellationToken) 什么时候会抛出 TaskCanceledException 异常?

8
根据文档,当任务被取消时,Task.Run(Action, CancellationToken)会抛出TaskCanceledException异常。
那么什么情况下会抛出TaskCanceledException异常呢?目前尚不清楚必须满足哪些条件才会抛出该异常。

1
你有检查你问题中链接的页面上的示例吗? - zerkms
1
@zerkms,我已经检查了示例。它显示TaskCanceledException是由Task.Wait()方法抛出的,而不是Task.Run()。我正在尝试理解在哪些条件下Task.Run()会抛出TaskCancelledException。 - Alex
2个回答

8

似乎存在一些混淆(文档可能有误导)。

调用 Task.Run 方法永远不会抛出 TaskCanceledException(至少在当前实现中是这样)。与 "操作参数为 null" 和 "与 cancellationToken 关联的 CancellationTokenSource 已释放。" 相反,它们同步抛出 ArgumentNullExceptionObjectDisposedException

Task.Run 返回一个 Task,可以使用 CancellationToken 参数取消它(更多关于取消的内容在此处),等待它以及使用 await tasktask.Wait()task.Result 等方式会抛出 TaskCanceledException(可能包装在 AggregateException 中)。

Task<int> task = null;
try
{
    task = Task.Run(() => 5, new CancellationToken(true));
}
catch (TaskCanceledException)
{
    Console.WriteLine("Unreachable code");
}

try
{
    int result = await task;
}
catch (TaskCanceledException)
{
    Console.WriteLine("Awaiting a canceled task");
}

如果文档有两个可能异常的部分,可能会更清晰:

  1. “常规”同步异常(例如ArgumentNullExceptionObjectDisposedException
  2. 只有通过等待返回的任务才能抛出的“异步”异步异常(例如TaskCanceledException

@l3arnon,这听起来非常合理。你是怎么发现调用Task.Run方法永远不会抛出TaskCanceledException的? - Alex
@Alex 嗯,这需要一些经验。但是如果你愿意深入研究,可以查看 Task.Run源代码,只会看到其他异常被同步抛出。 - i3arnon
1
文档太烂了。 - wayofthefuture
Task.Run 会返回一个 Task,可以使用 CancellationToken 参数取消它(有关取消的更多信息在此处),并使用 await task、task.Wait()、task.Result 等等等待它,但是这很误导人。只要源头取消而没有在用户代码中显式抛出,它就不会导致 await task 抛出 TaskCanceledException,除非在任务开始之前发生取消;至于 Wait,如果被取消,Wait() 不会抛出异常,但是 Wait(token) 会抛出异常。我还没有尝试过 Result,所以对此不做评论。 - Tomingsun
@Tomingsun 在取消的任务上Wait(和Result)会抛出一个包含通常聚合异常的TaskCanceledException。至于协作式取消...你是对的。不过我认为这不是问题的关键。欢迎您编辑答案并加以扩展。 - i3arnon

0

阅读托管线程中的取消

只有请求对象可以发出取消请求,每个监听器负责注意请求并及时响应。

然后继续说:

取消是协作的,并不强制执行监听器。 监听器确定如何在响应取消请求时优雅地终止。

您需要确保在CancellationToken.IsCancelledRequested或抛出异常的CancellationToken.ThrowIfCancelltionRequested上进行侦听。这是您的责任来取消和await操作以观察取消异常。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接