Whether to use TPL or async /await

There is an existing third party Rest API available which would accept one set of input and return the output for the same. (Think of it as Bing's Geo coding service, which would accept address and return location detail)

My need would be is to call this API multiple times (say 500-1000) for a single asp.net request and each call may take close to 500ms to return.

I could think of three approaches on how to do this action. Need your input on which could be best possible approach keeping speed as criteria.

1. Using Http Request in a for loop

Write a simple for loop and for each input call the REST API and add the output to the result. This by far could be the slowest. But there is no overhead of threads or context switching.

2. Using async and await

Use async and await mechanisms to call REST Api. It could be efficient as thread continues to do other activites while waiting for REST call to return. The problem I am facing is that, as per recommendations I should be using await all the way to the top most caller, which is not possible in my case. Not following it may lead to dead locks in asp.net as mentioned here http://msdn.microsoft.com/en-us/magazine/jj991977.aspx

3. Using Task Parallel Library

Using a Parallel.ForEach and use the Synchronuos API to invoke the Server parallely and use ConcurrentDictionary to hold the result. But may result in thread overhead

Also, let me know is there any other better way to handle things. I understand people might suggest to track performance for each approach, but would like to understand how people has solved this problem before


First, I think it's worth considering some issues that you didn't mention in your question:

  • 500-1000 API calls sounds like quite a lot. Isn't there a way to avoid that? Doesn't the API have some kind of bulk query functionality? Or can't you download their database and query it locally? (The more open organizations like Wikimedia or Stack Exchange often support this, the more closed ones like Microsoft or Google usually don't.)

    If those options are not available, then at least consider some kind of caching, if that makes sense for you.

  • The number of concurrent requests to the same server allowed at the same time in ASP.NET is only 10 by default. If you want to make more concurrent requests, you will need to set ServicePointManager.DefaultConnectionLimit.

  • Making this many requests could be considered abuse by the service provider and could lead to blocking of your IP. Make sure the provider is okay with this kind of usage.

Now, to your actual question: I think that the best option is to use async-await, even if you can't use it all the way. You can avoid deadlocks either by using ConfigureAwait(false) at every await (which is the correct solution) or by using something like Task.Run(() => /* your async code here */).Wait() to escape the ASP.NET context (which is the simple solution).

Using something like Parallel.ForEach() is not great, because it unnecessarily wastes ThreadPool threads.

If you go with async, you should probably also consider throttling. A simple way to achieve that is by using SemaphoreSlim.

The best solution is to use async and await, but in that case you will have to take it async all the way up the call stack to the controller action.

The for loop keeps it all sequential and synchronous, so it would definitely be the slowest solution. Parallel will block multiple threads per request, which will negatively impact your scalability.

Since the operation is I/O-based (calling a REST API), async is the most natural fit and should provide the best overall system performance of these options.

Need Your Help

Getting Mac system temperatures with Objective-C

objective-c macos hardware cpu

How do I access the system temperatures and fan speeds of a Mac using Objective-C? I have seen it done in applications like iStat, but I can not figure out how to do this. Does anyone know how?

How do I copy a JavaScript object into another object?

php javascript json object

Say I want to start with a blank JavaScript object: