Is HttpClient truly asynchronous or doing multithreading?

Topics: ASP.NET Web API
Oct 24, 2012 at 7:37 PM
Edited Oct 24, 2012 at 8:04 PM

I really doubted to ask this here but couldn't resist the temptation. I might be totally off base here and if I am, please forgive me and ignore this.

When I decompiled  the System.Net.Http.dll and looked at the HttpClientHandler's (the default handler for the HttpClient) SendAsync method, I see the following code which I believe is the code that actually makes the network call:

Task.Factory.StartNew(this.startRequest, requestState);

The HttpClientHandler uses HttpWebRequest under the hood and we know that even the HttpWebRequest's asynchronous begin* APM methods are not truly async (ref: http://social.msdn.microsoft.com/Forums/en-US/async/thread/9f75f92c-9a51-4921-bad5-97c2b15870bd) So, the above code makes sense why the HttpClient doesn't introduce any blocking on the main thread.

Correct me if I am wrong but Task.Factory.StartNew here picks a thread from the ThreadPool and processes the operation with that thread which basically means that we will be doing multithreading and this is obviously bad for a server application (depending on the situation of course). 

My question is:

Is this the case here or there are some other things that I am missing?

Oct 24, 2012 at 8:26 PM
Correct me if I am wrong but Task.Factory.StartNew here picks a thread from the ThreadPool and processes the operation with that thread which basically means that we will be doing multithreading and this is obviously bad for a server application (depending on the situation of course). 

What's inherently wrong with multithreading on the server?  Even if the operation were fully async the eventual completion would still happen on a ThreadPool thread.

The primary point is as you've said, don't block the caller in what they thought was a fully async operation.  In this situation we know we can't be fully async, so we offload to another thread so they can get on with their business.  If they have no other business then their thread completes and they wait for a callback, net gain zero threads.

Not blocking the caller is most critical on the client side UI thread, but is still important for other types of applications.

Oct 24, 2012 at 9:04 PM
Edited Oct 24, 2012 at 11:03 PM

Hi Tratcher,

Thanks for the answer.

I understand the concern now. As you pointed out, we can't be fully async there (I am not sure why but I believe there is a reason), so multithreading is better completely blocking.

What I always know is that when there is an unnecessary thread hop (I understand that the thread hop introduced with HttpClient is not unnecessary comparing completely blocking) for a server application, the performance will be reduced when I get a lot of traffic (not sure how many is a lot here). I am not saying I am correct here but this is what I know, so that's why I was concerned. 

Assume that my ASP.NET application received a request and Thread A started handling it. Then, I made a network call with the HttpClient and at some point Thread B will pick that up. Also assume that I am handing the async right here and in that case, Thread A should go back to the ThreadPool till the HttpClient finishes the request. Now, as there are some blocking operations that the HttpWebRequest's BeginGetResponse method is doing, instead of Thread A, the Thread B will be blocked at some point till operation gets truly async (sorry, magic happens there for me and I don't have a lot info what is actually going on from now on). At that point, the Thread B will also be freed up till the result comes back. When the result comes back to any thread inside the ThreadPool, it will do whatever it needs registered as callback and hand result off to the Thread A (assuming we want to get back to the sync context, we should eventually end up with Thread A). 

So, AFAIK, we will be still blocking a thread inside the ThreadPool and we will introduce a thread hop. Is my understanding wrong here?

Also, based on your answer on the MSDN thread that I have linked above, do u guys have any plan on making the entire HTTP request pipeline truly async or will HttpClient always work with HttpWebRequest?

Oct 24, 2012 at 10:11 PM
Edited Oct 24, 2012 at 10:12 PM

Your description sounds about right, but I will clarify it a bit.  When you do an async operation with HttpWebRequest it looks something like this:

Thread 1: BeginGetResponse() -> Resolve proxies -> Dns -> ConnectAsync

Thread 2: Connect callback -> Send request -> ReceiveAsync

Thread 3: Receive callback -> Process response -> Invoke user's callback.

You don't end up back on Thread 1 regardless, though yes your context does get carried over.

Adding HttpClient just adds a Thread 0 to the mix that calls Task.Run as you observed.  You'll still end up on Thread 3 eventually.

Cleaning up HTTP is definitely on the TODO list, but it's not clear yet when that will happen.  Even then, the biggest changes that would be made would be to take some of the items from BeginGetResponse and make them behave more async, such as proxies and DNS.  This will end up making you switch threads more, not less, but will avoid blocking any threads unnecessarily.