我创建了以下内容,以便在超时的情况下执行多个异步任务。我正在寻找一种可以从任务中提取结果的方法——只提取那些超过超时的结果,而不管其他任务是否失败(简化):
TimeSpan timeout = TimeSpan.FromSeconds(5.0);
Task<Task>[] tasksOfTasks =
{
Task.WhenAny(SomeTaskAsync("a"), Task.Delay(timeout)),
Task.WhenAny(SomeTaskAsync("b"), Task.Delay(timeout)),
Task.WhenAny(SomeTaskAsync("c"), Task.Delay(timeout))
};
Task[] completedTasks = await Task.WhenAll(tasksOfTasks);
List<MyResult> = completedTasks.OfType<Task<MyResult>>().Select(task => task.Result).ToList();
我已经在(Web API)服务器中的一个非静态类中实现了这一点。
这在第一次调用中运行良好,但是额外的调用导致completedTasks
奇怪地累积了以前对服务器的调用中的任务(如调试器所示)。在第二次通话中,有6项任务已完成,在第三次通话中有9项任务,依此类推
我的问题:
- 知道为什么吗?我想这是因为以前的任务没有被取消,但这段代码在一个类的新实例中
- 知道如何避免这种积累吗
附言:请参阅我对此问题的回答。
我无法使用我的心理调试来理解为什么您的代码"导致completedTasks
从以前的调用中奇怪地累积任务",但它可能暴露了您的一些误解。
下面是一个基于代码的工作示例(使用string
而不是MyResult
):
Task<string> timeoutTask =
Task.Delay(TimeSpan.FromSeconds(5)).ContinueWith(_ => string.Empty);
Task<Task<string>>[] tasksOfTasks =
{
Task.WhenAny(SomeTaskAsync("a"), timeoutTask),
Task.WhenAny(SomeTaskAsync("b"), timeoutTask),
Task.WhenAny(SomeTaskAsync("c"), timeoutTask)
};
Task<string>[] completedTasks = await Task.WhenAll(tasksOfTasks);
List<string> results = completedTasks.Where(task => task != timeoutTask).
Select(task => task.Result).ToList();
那么,不同之处在于:
- 我对所有
WhenAny
调用使用相同的超时任务。没有必要使用更多,它们可以在稍微不同的时间内完成 - 我让超时任务返回一个值,所以它实际上是
Task<string>
,而不是Task
- 这使得每个
WhenAny
调用也返回一个Task<string>
(并且tasksOfTasks
是Task<Task<string>>[]
),这将使得实际返回这些任务的结果成为可能 - 在
await
ing之后,我们需要过滤掉返回超时任务的WhenAny
调用,因为使用completedTasks.Where(task => task != timeoutTask)
不会有结果(只有string.Empty
)
p.S:我也回答了这个问题,我会(令人惊讶地)建议您使用我的解决方案。
注意:不建议使用Task.Result
属性。您应该await
它(即使您知道它已经完成)