编写一个Task.WhenAll/WhenAny的变体,以在第一个出现故障/取消的任务上取消所有其他任务

3
我对C#还不是很熟悉,今天开始尝试使用TPL。我决定写一个修改过的Task.WhenAll作为一个练习。我希望它具有以下行为:
  • 发现第一个失败或被取消的任务时,取消其余任务而不是等待它们完成。
  • 如果任务失败,则返回的任务应设置正确的异常(即不通过继续运行并替换为OperationCancelledException())
  • 方法签名中没有异步关键字(想避免将其传递上去)。
我想出了下面这段疯狂/愚蠢的代码,它无法正常工作,而且我很难想象发生了什么。我无法想象会发生任何阻塞,我设想的是一系列任务,每个任务都在等待其他任务完成。能否有人解释一下发生了什么?
在实际生产环境中我不会使用它,这只是为了测试我的基本功。我意识到更简单的方法是进行Task.WhenAll,并使列表中的任务本身具有在失败时执行取消的连续性。
    public static Task WhenAllError(List<Task> tasks, CancellationToken ct)
    {
        var tcs = new TaskCompletionSource<object>();
        return Task.WhenAny(tasks).ContinueWith<Task>((t) =>
             {
                 if (tasks.Count == 0)
                 {
                     tcs.SetResult(null);
                     return tcs.Task;
                 }

                 if (t.IsFaulted)
                 {
                     Console.WriteLine("Task faulted. Cancelling other tasks: {0}", t.Id);
                     cts.Cancel();
                     // Make sure the tasks are cancelled if necessary
                     tcs.SetException(t.Exception);
                     return tcs.Task;
                 }
                 // Similarly handle Cancelled

                 tasks.Remove(t);
                 return WhenAllError(tasks, ct);
             }).Unwrap();
    }

1个回答

2
CancellationToken类没有Cancel方法。你需要一个CancellationTokenSource来取消CancellationToken
同样地,要影响任务的结果,你需要一个TaskCompletionSource,你不能安全地取消已经运行的任务。参见这个帖子

是的,我知道 :). 因此名称为 cts/ct。我没有包含代码的那部分。另外,您不能中止已经运行的任务,但是您可以取消并等待任务被取消(因此有这个注释:“// Make sure the tasks are cancelled if necessary”。 - Panther
我正在寻找有关任务本身如何被调度、运行、等待等方面的解释。是否存在阻塞?用户拥有一个代理任务(Unwrap)的引用,并假设他在等待它,那么将创建的结果任务会发生什么?我们是否也必须等待所有这些任务完成?或者最后一个任务的完成是否会在任务间传播到用户正在等待的最外层任务? - Panther

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