如何将Task.WhenAll()用于多个不同返回类型的列表?

时间:2016-01-24 08:35:27

标签: c# generics async-await task-parallel-library task

我有两组任务,每组都有不同的结果类型:

IEnumerable<Task<T1>> set1 = GetTasksOfT1();
IEnumerable<Task<T2>> set2 = GetTasksOfT2();

现在我想在一行中等待两个集合,但我必须使用强制转换来设置set1:

 await Task.WhenAll(set1.Select(p => p as Task).Concat(set2));

那是因为如果我不使用演员,我会收到此错误:

IEnumerable<Task<T1>>' does not contain a definition for 'Concat' 
and the best extension method overload 'Queryable.Concat<Task<T2>>(IQueryable<Task<T2>>, IEnumerable<Task<T2>>)' 
requires a receiver of type 'IQueryable<Task<T2>>'

这是显而易见的。

那么有没有一种方法可以使用单个WhenAll()而无需强制转换?

编辑: 返回类型T1和T2 非常重要 - 我等待任务后消耗它们:

//After await Task.WhenAll(...):
var t1Results = await set1; // all set1 Tasks are completed by now 
var t2Results = await set2; // same
DoSomethingWithResults(t1Results, t2Results);

4 个答案:

答案 0 :(得分:2)

实际上,编译器只是抱怨类型推断失败。你可以用它来帮助它:

IEnumerable<Task<int>> set1 = null;
IEnumerable<Task<string>> set2 = null;

set1.Concat((IEnumerable<Task>)set2); //#1
((IEnumerable<Task>)set1).Concat(set2); //#2
set1.Concat<Task>(set2); //#3

还有许多其他方法可以提供类型信息。这是有效的,因为IEnumerable是协变的。这将调用Enumerable.Concat<Task>(IEnumerable<Task>, IEnumerable<Task>)

答案 1 :(得分:1)

如果GetTasksOfT1GetTasksOfT2这两种方法都不会返回Task<T>Task,那么您就不会有任何问题。

所以我建议重构这些方法来返回一系列Task对象,IEnumerable<Task>或者拾取Kote的解决方案。

答案 2 :(得分:0)

如果您需要两组Task的结果,那么不等待所有列表可能会更简单。

我想你的代码(使用usr&#39的建议)看起来像这样:

IEnumerable<Task<T1>> set1 = GetTasksOfT1();
IEnumerable<Task<T2>> set2 = GetTasksOfT2();

await Task.WhenAll(set1.Concat<Task>(set2));

var t1Results = await Task.WhenAll(set1);
var t2Results = await Task.WhenAll(set2);

在这里,合并的await并没有真正起到任何作用。如果删除它,两个结果变量仍然会被正确初始化,因为它们有自己的await

答案 3 :(得分:0)

虽然如果创建一个数组比连接两个枚举更有争议,你可以将连接保留在等式之外:

await Task.WhenAll(set1, set2);

但是,既然你需要的是评估每个任务序列的结果,我就不用费心去等待它们了:

DoSomethingWithResults(await set1, await set2);