使用 WhenAll 和 ContinueWith 时任务结果的顺序是什么

本文关键字:结果 顺序 是什么 任务 WhenAll ContinueWith 使用 | 更新日期: 2023-09-27 18:32:58

只是想知道使用 WhenAll 和 ContinueWith 时任务结果的顺序是什么。这些结果是否与任务 ID 的顺序相同?

我在下面写了代码

public static async Task<string> PrintNumber(int number)
{
    return await Task.Delay(number*1000).ContinueWith(_=>
    {
        Console.WriteLine(number);return "TaskId:"+Task.CurrentId+" Result:"+number;
    });
}
public static void Main()
{
    Task.WhenAll(new[]
    {
        PrintNumber(3),
        PrintNumber(2),
        PrintNumber(1),
    }).ContinueWith((antecedent) => 
    { 
        foreach(var a in antecedent.Result)
        {
            Console.WriteLine(a);
        }
    });
}

并在 linqpad 中运行几次以获得相同的结果

1
2
3
TaskId:15 Result:3
TaskId:14 Result:2
TaskId:13 Result:1

1
2
3
TaskId:18 Result:3
TaskId:17 Result:2
TaskId:16 Result:1

使用 WhenAll 和 ContinueWith 时任务结果的顺序是什么

通过该特定调用,Task[]的参数 - 顺序无法保证。

事实上,根据Task.WhenAll(Task[])文件,根本没有提到秩序。但是,如果您使用Task.WhenAll(IEnumerable<Task<TResult>>)重载,则其内容如下:

如果没有任何任务出错,并且没有取消任何任务,则生成的任务将以 RanToCompletion 状态结束。返回任务的结果将被设置为一个数组,其中包含所提供任务的所有结果,其顺序与提供顺序相同(例如,如果输入任务数组包含 t1、t2、t3,则输出任务的结果将返回一个 TResult[],其中 arr[0] == t1。结果,到达 arr1 == t2。结果,和 arr[2] == t3。结果)。

调用Task.WhenAll(使用枚举数组或参数数组)时,结果的顺序与传递给该方法的任务的顺序匹配。

也就是说,这是真的:

var task1 = PrintNumber(3);
var task2 = PrintNumber(2);
var task3 = PrintNumber(1);
var taskResults = await Task.WhenAll(task1, task2, task3);
// taskResults[0] is the same as task1.Result
// taskResults[1] is the same as task2.Result
// taskResults[2] is the same as task3.Result

然而,ContinueWith是一个完全不同的故事。 ContinueWith附加一个延续,此延续将在任务完成后的某个时间运行。

在您的特定代码中,您不会将延续附加到传递给 Task.WhenAll 的任务。但是,如果您是,那么该延续可以在该任务完成后的任何时间运行。

附带说明一下,不要使用ContinueWith(正如我在博客上解释的那样)。只需改用await;生成的代码更正确、更清晰、更易于维护。