2016-02-15 182 views
2

我想围绕如何在foreach循环中处理多个异步/等待调用。我有大约20,000行由foreach循环处理的数据。大约我的代码是:在foreach循环迭代中多次异步/等待调用

foreach (var item in data) 
{ 
    if (ConditionA(item)) 
    { 
     if (ConditionAB(item)); 
     { 
      await CreateThingViaAPICall(item) 
     } 
     else 
     { 
      var result = await GetExistingRecord(item); 
      var result2 = await GetOtherExistingRecord(result); 
      var result3 = await GetOtherExistingRecord(result2); 
      //Do processing 
      ...   
      await CreateThingViaAPICall(); 
     } 
    } 
    ... and so on   
} 

我看过很多帖子说要使用异步在一个循环是建立任务列表,然后使用Task.WhenAll的最佳途径。在我的情况下,我有任务相互依赖作为每个迭代的一部分。在这种情况下,如何构建要执行的任务列表?

+0

你有什么问题? –

+0

如果我没有弄错,在foreach中使用异步/等待的推荐方法是首先构建任务列表,然后调用Task.WhenAll。我的问题是,我有多个任务循环的每个迭代相互依赖。在这种情况下,我如何建立一个任务列表来等待? – Jacob

+0

我误解了你的问题吗?迭代中的每个项目是否取决于前一项的成功完成? –

回答

3

如果你打破个别项目的处理到一个单独的(异步)方法最简单的方法:

private async Task ProcessItemAsync(Item item) 
{ 
    if (ConditionA(item)) 
    { 
     if (ConditionAB(item)); 
     { 
      await CreateThingViaAPICall(item) 
     } 
     else 
     { 
      var result = await GetExistingRecord(item); 
      var result2 = await GetOtherExistingRecord(result); 
      var result3 = await GetOtherExistingRecord(result2); 
      //Do processing 
      ...   
      await CreateThingViaAPICall(); 
     } 
    } 
    ... and so on 
} 

然后处理您的收藏像这样:

var tasks = data.Select(ProcessItemAsync); 
await Task.WhenAll(tasks); 

这有效地包装了多个从属将单个项目处理为一个任务所需的任务,允许这些步骤顺序发生,同时集合本身的项目同时处理。

由于成千上万的项目中有10个,出于各种原因,您可能会发现需要限制同时运行的任务数量。看看这种情况下的TPL Dataflow。示例见here

+2

好吧,这是有道理的,并与@ jon-hanna的答案相结合更有意义。 – Jacob

+0

我理解你的问题的方式是可以同时处理项目A和项目B,但涉及处理单个项目的步骤必须按顺序进行。这就是这种方法给你的。 –

1

如果我没有弄错,在foreach中使用异步/等待的推荐方法是首先创建一个任务列表,然后调用Task.WhenAll。

你有部分错误。

如果你有多个任务不依赖于对方,那么在WhenAll中执行这些多任务通常是一个非常好的主意,这样他们就可以一起调度,从而提供更好的吞吐量。

但是,如果每个任务取决于前一个的结果,那么这种方法是不可行的。相反,你应该只是await他们在foreach

事实上,这对任何情况都可以正常工作,如果他们不需要,任务之间相互等待就不太理想。

foreach的能力await任务其实是最大的收获一个async/await给了我们。大多数使用await的代码可以很容易地重写为ContinueWith,如果不那么优雅,但循环更复杂,并且只有通过检查任务的结果才能找到实际的循环结尾,再次更复杂。

+0

因此,建议使用Task.WhenAll是一个性能相关的建议,而不是'坏的事情会发生,如果你这样做'的建议? – Jacob

+1

是的。现在,当它适用时,它可能会产生很大的差异,所以当然可以采取这种方法。你甚至可以做一个混合;大块中的每个块都依赖于每个块,但每个块可以独立。 –