是使用TPL还是async / await

时间:2014-02-27 09:28:05

标签: asp.net .net asynchronous task-parallel-library async-await

现有第三方Rest API可用,它接受一组输入并返回相同的输出。 (将其视为Bing的地理编码服务,它将接受地址并返回位置详细信息)

我需要的是为一个asp.net请求多次调用此API(比如500-1000),每次调用可能需要接近500毫秒才能返回。

我可以想到如何采取这种行动的三种方法。需要您的输入,这可能是保持速度作为标准的最佳方法。

1。在for循环中使用Http Request

编写一个简单的for循环,并为每个输入调用REST API并将输出添加到结果中。到目前为止,这可能是最慢的。但是没有线程或上下文切换的开销。

2。使用async并等待

使用async和await机制来调用REST Api。当等待REST调用返回时,线程继续执行其他活动可能会有效。我面临的问题是,根据建议,我应该使用等待一直到最顶层的调用者,这在我的情况下是不可能的。不遵循它可能会导致asp.net死锁,如http://msdn.microsoft.com/en-us/magazine/jj991977.aspx

所述

第3。使用任务并行库

使用Parallel.ForEach并使用Synchronuos API并行调用服务器并使用ConcurrentDictionary保存结果。但可能导致线程开销

另外,让我知道还有其他更好的办法来处理事情。我理解人们可能会建议跟踪每种方法的性能,但是想了解人们如何解决这个问题

2 个答案:

答案 0 :(得分:4)

最佳解决方案是使用asyncawait,但在这种情况下, 必须将async一直带到调用堆栈对控制器的行动。

for循环使所有顺序和同步,所以它肯定是最慢的解决方案。 Parallel将阻止每个请求多个线程,这将对您的可伸缩性产生负面影响。

由于操作是基于I / O的(调用REST API),async是最自然的选择,应该提供这些选项的最佳整体系统性能。

答案 1 :(得分:2)

首先,我认为值得考虑一些你在问题中未提及的问题:

  • 500-1000 API调用听起来非常多。有没有办法避免这种情况? API是否具有某种批量查询功能?或者你不能下载他们的数据库并在本地查询? (维基媒体或Stack Exchange等更开放的组织经常支持这种情况,像微软或谷歌这样的封闭组织通常不支持这种情况。)

    如果这些选项不可用,那么至少考虑某种缓存,如果这对你有意义的话。

  • 默认情况下,ASP.NET中同时允许同一服务器的并发请求数仅为10。如果您想要发出更多并发请求,则需要设置ServicePointManager.DefaultConnectionLimit

  • 提出这么多请求可能会被服务提供商视为滥用,并可能导致您的IP被阻止。确保提供商可以使用这种用途。

现在,针对您的实际问题:我认为最好的选择是使用async - await,即使您无法一直使用它。您可以通过在每ConfigureAwait(false)使用await(这是正确的解决方案)或使用Task.Run(() => /* your async code here */).Wait()之类的东西来逃避ASP.NET上下文(这是简单的解决方案)来避免死锁

使用像Parallel.ForEach()这样的东西并不是很好,因为它不必要地浪费了ThreadPool线程。

如果你选择async,你应该考虑限制。实现这一目标的一种简单方法是使用SemaphoreSlim