处理在 Task.WhenAll().Wait() 指定超时后完成的任务

Handle tasks which complete after Task.WhenAll().Wait() specified timeout

我正在尝试使用 Task.WhenAll(tasks).Wait(timeout) 来等待任务完成,然后再处理任务结果。

考虑这个例子:

var tasks = new List<Task<Foo>>();

tasks.Add(Task.Run(() => GetData1()));
tasks.Add(Task.Run(() => GetData2()));

Task.WhenAll(tasks).Wait(TimeSpan.FromSeconds(5));

var completedTasks = tasks
    .Where(t => t.Status == TaskStatus.RanToCompletion)
    .Select(t => t.Result)
    .ToList();

// Process completed tasks
// ...

private Foo GetData1()
{
    Thread.Sleep(TimeSpan.FromSeconds(4));
    return new Foo();
}

private Foo GetData2()
{
    Thread.Sleep(TimeSpan.FromSeconds(10));
    // How can I get the result of this task once it completes?
    return new Foo();
}

这些任务之一可能无法在 5 秒超时内完成执行。

是否可以以某种方式处理在指定超时后完成的任务的结果?也许我在这种情况下没有使用正确的方法?

编辑:

我正在尝试获取在指定超时时间内完成的所有任务结果。在 Task.WhenAll(tasks).Wait(TimeSpan.FromSeconds(5)):

之后可能会有以下结果
  1. 第一个任务在 5 秒内完成。
  2. 第二个任务在 5 秒内完成。
  3. 两项任务都在 5 秒内完成。
  4. None 个任务在 5 秒内完成。是否有可能获得 5 秒内未完成但稍后完成的任务结果,比如说 10 秒后?

而不是 Waitall,您可能只想做一些 5 秒的 Spin/sleep,然后像上面那样查询列表。

然后您应该能够在几秒钟后再次枚举以查看还完成了什么。

如果性能是一个问题,您可能需要额外的 'wrapping' 来查看所有任务是否在 5 秒之前完成。

最后在删除他答案的用户的帮助下,我得到了这个解决方案:

private const int TimeoutInSeconds = 5;
private static void Main(string[] args)
{
    var tasks = new List<Task>() 
    {
        Task.Run( async() => await Task.Delay(30)),
        Task.Run( async() => await Task.Delay(300)),
        Task.Run( async() => await Task.Delay(6000)),
        Task.Run( async() => await Task.Delay(8000))
    };

    Task.WhenAll(tasks).Wait(TimeSpan.FromSeconds(TimeoutInSeconds));

    var completedTasks = tasks
        .Where(t => t.Status == TaskStatus.RanToCompletion).ToList();
    var incompleteTasks = tasks
        .Where(t => t.Status != TaskStatus.RanToCompletion).ToList();

    Task.WhenAll(incompleteTasks)
        .ContinueWith(t => { ProcessDelayedTasks(incompleteTasks); });

    ProcessCompletedTasks(completedTasks);
    Console.ReadKey();
}

private static void ProcessCompletedTasks(IEnumerable<Task> delayedTasks)
{
    Console.WriteLine("Processing completed tasks...");
}

private static void ProcessDelayedTasks(IEnumerable<Task> delayedTasks)
{
    Console.WriteLine("Processing delayed tasks...");
}

我认为在

之间可能会丢失任务物品
var completedTasks = tasks.Where(t => t.Status == TaskStatus.RanToCompletion).ToList();

var incompleteTasks = tasks.Where(t => t.Status != TaskStatus.RanToCompletion).ToList();

因为有些任务可能会在这段时间内运行完成。

作为解决方法(虽然不正确),您可以交换这些行。在这种情况下,某些任务可能会出现在每个(已完成任务和未完成任务)列表中。但也许这总比完全迷失要好。

用于比较已启动任务的数量以及 completedTasks 和 incompleteTasks 列表中的任务数量的单元测试也可能有用。