在C#中使用内部异步调用暂停循环的最佳方法

时间:2019-04-17 15:34:07

标签: c# async-await dotnet-httpclient

我正在向API发送大约100,000个JSON对象的列表,该API只能一个接一个地接受它们,并且我以异步方式发送它们。我知道API在内部将接收到的对象发送到一个队列,该队列似乎被所有这些请求阻塞了,导致我在经过许多请求后收到“网关超时”错误。

我尝试将列表拆分成不同大小的批次,并在发送每个批次后将线程置于睡眠状态,但最终发生的结果是,它在大约批量大小时失败并出现相同的错误,我尝试了批次3000、2500和1000具有相同的结果,并且线程似乎从未进入睡眠状态。

这是有问题的代码:

public async Task TransferData(IEnumerable<MyData> data)  
{  
     var pages = Math.Ceil(data.Count() / 3000m);  

     for (var page = 0; page < pages; page++)  
     {  
         await TransferPage(data.Skip(page * 3000).Take(3000);
         Thread.Sleep(10000);  
     }  
}

private async Task TransferPage(IEnumerable<MyData> data)  
{  
     await Task.WhenAll(data.Select(p => webConnection.PostDataAsync(JsonConvert.SerializeObject(p, Formatting.None))));  
}

注意:webConnection只是一个类,该类具有已实例化的HttpClient,并执行PostAsync将数据发送到预期的URL。

对TransferData的调用是在控制台应用程序中完成的,如下所示:

try  
{  
   ...    
   dataManager.TransferData(data).Wait();
}
catch(AggregateException ex)
{
   ...
}
catch(Exception ex)
{
   ...
}

谢谢您的指导。

更新:澄清在评论中引起的一些混淆。外部API正在一个接一个地接收对象,如果您在WhileAll IEnumerable具有Select的内部查看私有方法TransferPage ,则该方法将对内部执行实际HttpClient PostAsync的方法的调用进行选择。因此,将对象按批进行分组,并在每批中将它们一一发送。我希望这可以使它更加清楚。

1 个答案:

答案 0 :(得分:0)

可能发生的情况是,一个或多个PostDataAsync任务引发超时错误,从而导致任务失败。 Task.WhenAll仅将它们捆绑成一个AggregateException,并在列表中的所有任务完成后将其抛出,这就是为什么只在批处理结束时看到异常的原因。

尽管您试图节流,但您仍可能不堪重负。您可能应该做几件事:

  • 改善异常处理和重试情况。您可以在PostDataAsync内部和/或外部执行此操作。即使您没有压倒性的服务,您仍然需要处理临时异常以应对网络故障等。
  • 用适当的限制实现替换批处理逻辑。 Serg在评论中链接的问题的答案是一个好的开始-SemaphoreSlim或TPL Dataflow是常见的解决方案。