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

时间:2017-12-11 16:43:35

标签: c# task-parallel-library

我试图让C#中的任务适用于特定用例,但我不理解任务延续选项如何影响任务流。

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

A - > B - > C - > d

但是,我想在错误发生时包含短路选项,所以看起来应该是这样的:

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

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

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

我想要发生的是A到D跑,如果其中一个导致取消,跳过其余的并运行X.如果A到D完成,任务不应该取消。该解决方案需要支持任意数量的continuation,并且将使用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);

此处的预期行为是返回3,状态未完成。

实际结果是任务被取消。

我该怎么做?

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

2 个答案:

答案 0 :(得分:1)

想出来 - 无论前面的延续是否完成而没有将状态设置为取消,最后一次继续运行都是这样做的:

  1. 将最后一个延续的继续选项更改为TaskContinuation.None,以便它始终运行,因此如果状态为已完成,则不会取消。

  2. 不要将取消令牌传递给最后一个延续,因为传入已取消的取消令牌似乎会导致取消继续取消,如果它没有令牌就会运行

答案 1 :(得分:0)

有关此行为的说明,请参阅ContinueWith的备注:

  

在当前任务完成之前,不会安排返回的任务执行。如果不满足通过continuationOptions参数指定的条件,则将取消继续任务而不是已安排。

由于未达到上次ContinueWith来电的条件,因此该通话返回的Task已取消。