在 Task.WhenAll() 调用后获取结果。

4

Task.WhenAll(params System.Threading.Tasks.Task[] tasks)返回一个Task,但是在调用此方法后,获取任务结果的正确方式是什么? 等待该任务完成后,可以通过再次等待原始任务来获取结果,这应该是可以的,因为任务已经完成。另外,也可以使用Task.Result属性来获取结果,但这通常被认为不是好的做法。

Task<TResult1> task1= ...
Task<TResult2> task2= ...
Task<TResult3> task3= ...

await Task.WhenAll(task1, task2, task3)
var a = task1.Result; // returns TResult1
var b = await task1; // also returns TResult1

在这里我应该选择哪一个,为什么呢?


你的意思是说你找不到这个重载?你的foreach循环正在处理单个任务,而不是Task.WhenAll的结果(你正在丢弃它)。 - Jeroen Mostert
我编辑了最初的问题,因为它意味着有不同的方法参数。我丢弃了 Task.WaitAll 的结果,因为它只返回重载中选择的任务。 - Autiarii
2个回答

5

如果您只有一个 IEnumerable<Task<TResult>>,并且任务将会 即时创建(例如由于 .Select()),则您需要执行两次任务。

因此,请确保要么向 Task.WhenAll() 提供已经实例化的集合,要么从该方法的返回值中获取结果:

var someTasks = Enumerable.Range(1, 10).Select(i => { Task.Delay(i * 100); return i; });

// Bad style, cause someTasks is an IEnumerable created on-the-fly
await Task.WhenAll(someTasks);
foreach(var task in someTasks)
{
    var taskResult = await task;
    Console.WriteLine(taskResult);
}

// Okay style, cause tasks are materialized before waiting, but easy to misuse wrong variable name.
var myTasks = someTasks.ToList();
await Task.WhenAll(myTasks);
foreach(var task in myTasks)
{
    Console.WriteLine(task.Result);
}

// Best style
var results = await Task.WhenAll(someTasks);
foreach(var result in results)
{
    Console.WriteLine(result);
}

更新

在你的问题中阅读到以下内容:

然而,我找不到除了Task之外返回任何内容的重载。

如果您在Task.WhenAll()方法中提供的任务集合没有共同的Task<T>类型,则会出现此情况。这可能会发生,例如,如果您想并行运行两个返回不同值的任务。 在这种情况下,您必须将任务具体化,然后逐个检查结果:

public static class Program
{
    public static async Task Main(string[] args)
    {
        var taskOne = ReturnTwo();
        var taskTwo = ReturnPi();

        await Task.WhenAll(taskOne, taskTwo);

        Console.WriteLine(taskOne.Result);
        Console.WriteLine(taskTwo.Result);

        Console.ReadKey();
    }

    private static async Task<int> ReturnTwo()
    {
        await Task.Delay(500);
        return 2;
    }

    private static async Task<double> ReturnPi()
    {
        await Task.Delay(500);
        return Math.PI;
    }
}

3
这里是返回 Task<TResult[]> 的重载方法 - MS Docs 示例:
static async Task Test()
{
    List<Task<string>> tasks = new List<Task<string>>();
    for (int i = 0; i < 5; i++)
    {
        var currentTask = GetStringAsync();
        tasks.Add(currentTask);
    }

    string[] result = await Task.WhenAll(tasks);
}

static async Task<string> GetStringAsync()
{
    await Task.Delay(1000);
    return "Result string";
}


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