如何在不触发取消本身的情况下处理取消的任务?

How to handle tasks that are canceled without triggering a cancellation itself?

我正在尝试让 C# 中的任务为特定用例工作,但我不了解任务继续选项如何影响任务流。

我想要做的是将一系列任务与 ContinueWith 链接在一起。这看起来像这样:

A -> B -> C -> D

但是,我想包含在出现错误时将其短路的选项,因此它应该如下所示:

A -> B -> C -> D -> X

所以我将 "OnlyOnRanToCompletion" 作为每个 ContinueWith 函数的任务继续选项。然后,为了捕获取消和 return 错误,我将最终任务放在链的末尾,并将任务继续选项设置为 "OnlyOnCanceled".

问题是,当最后一个块被击中时,不满足继续选项,即使原始任务系列从未被取消,任务也会被设置为取消。

我想要的是 A 到 D 运行,如果其中一个导致取消,则跳过其余部分和 运行 X。如果 A 到 D 完成,任务不应该取消。该解决方案需要支持任意数量的延续,并将使用 LINQ.Expressions 创建,因此使用 async/await 可能不会成功,除非它创造性地完成。

展示此内容的一些示例代码是:

var cts = new CancellationTokenSource();
var token = cts.Token;

var t = Task.FromResult(1)
  .ContinueWith(
    x => x.Result + 1,
    token,
    TaskContinuationOptions.OnlyOnRanToCompletion,
    TaskScheduler.Default)
  .ContinueWith(
    x => x.Result + 1,
    token,
    TaskContinuationOptions.OnlyOnRanToCompletion,
    TaskScheduler.Default)
  .ContinueWith(
    x => -1,
    token,
    TaskContinuationOptions.OnlyOnCanceled,
    TaskScheduler.Default);

这里的预期行为是 return 3,状态未完成。

实际结果是任务被取消

我该怎么做?

此外,我不能使用 async,因为我的目标是在从 LINQ.Expressions 编译的内容中搭载 TPL,以便它可以异步评估并在最后处理错误而不抛出任何错误例外。

有关此行为的解释,请参阅 ContinueWith 的备注:

The returned Task will not be scheduled for execution until the current task has completed. If the criteria specified through the continuationOptions parameter are not met, the continuation task will be canceled instead of scheduled.

由于您上次 ContinueWith 调用的条件不满足,因此从该调用返回的 Task 被取消。

弄清楚了 - 要将最后一个延续到 运行,而不管之前的延续是否完成并且不将状态设置为取消,请执行此操作:

  1. 将最后一个continuation的continuation选项改成TaskContinuation.None,这样它总是运行s,所以如果它以完成状态到达这里就不会取消.

  2. 不要将取消令牌传递给最后一个延续,因为传递已取消的取消令牌似乎会导致继续取消,否则的话 运行 没有令牌。