【问题标题】:How to use Task.WhenAll() for multiple Lists of different return types?如何将 Task.WhenAll() 用于不同返回类型的多个列表?
【发布时间】:2016-04-30 15:32:04
【问题描述】:

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

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);

【问题讨论】:

  • T1T2 是否都遵循通用接口?
  • @Clockwork-Muse 请看我的编辑。但是,使用阻塞方法Task.WaitAll() 是一个非常糟糕的建议。
  • @Shay__ - 你是对的,这是个坏建议。

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


【解决方案1】:

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

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&lt;Task&gt;(IEnumerable&lt;Task&gt;, IEnumerable&lt;Task&gt;)

【讨论】:

  • 很好的答案!我会选择#3 :)
【解决方案2】:

如果GetTasksOfT1GetTasksOfT2 这两种方法都不会返回Task&lt;T&gt;Task,你不会有任何问题。

所以我建议要么重构这些方法以返回一系列 Task 对象,IEnumerable&lt;Task&gt;,要么选择 Kote 的解决方案。

【讨论】:

  • 不,我需要在任务完成后使用这些结果。
【解决方案3】:

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

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

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 并没有真正起到任何作用。如果删除它,两个结果变量仍将正确初始化,因为它们有自己的awaits。

【讨论】:

    【解决方案4】:

    虽然创建一个数组是否比连接两个可枚举项更好值得商榷,但您可以将连接排除在等式之外:

    await Task.WhenAll(set1, set2);
    

    但是,由于您需要的是每个任务序列的评估结果,所以我不会费心将它们作为一个集合等待:

    DoSomethingWithResults(await set1, await set2);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-20
      • 1970-01-01
      • 2022-11-17
      相关资源
      最近更新 更多