kriper
kriper

Reputation: 1054

HttpClient provide not truly async operations?

I'm confused about async IO operations. In this article Stephen Cleary explains that we should not use Task.Run(() => SomeIoMethod()) because truly async operations should use

standard P/Invoke asynchronous I/O system in .NET

http://blog.stephencleary.com/2013/11/there-is-no-thread.html

However, avoid “fake asynchrony” in libraries. Fake asynchrony is when a component has an async-ready API, but it’s implemented by just wrapping the synchronous API within a thread pool thread. That is counterproductive to scalability on ASP.NET. One prominent example of fake asynchrony is Newtonsoft JSON.NET, an otherwise excellent library. It’s best to not call the (fake) asynchronous versions for serializing JSON; just call the synchronous versions instead. A trickier example of fake asynchrony is the BCL file streams. When a file stream is opened, it must be explicitly opened for asynchronous access; otherwise, it will use fake asynchrony, synchronously blocking a thread pool thread on the file reads and writes.

And he advises to use HttpClient but internaly it use Task.Factory.StartNew() enter image description here

Does this mean that HttpClient provides not truly async operations?

Upvotes: 5

Views: 647

Answers (2)

Stephen Cleary
Stephen Cleary

Reputation: 456947

Does this mean that HttpClient provides not truly async operations?

Sort of. HttpClient is in an unusual position, since it's primary implementation uses HttpWebRequest, which is only partially asynchronous.

In particular, the DNS lookup is synchronous, and I think maybe the proxy resolution, too. After that, it's all asynchronous. So, for most scenarios, the DNS is fast (usually cached) and there isn't a proxy, so it acts asynchronously. Unfortunately, there are enough scenarios (particularly from within corporate networks) where the synchronous operations can cause significant lag.

So, when the team was writing HttpClient, they had three options:

  1. Fix HttpWebRequest (and friends) allowing for fully-asynchronous operations. Unfortunately, this would have broken a fair amount of code. Due to the way inheritance is used as extension points in these objects, adding asynchronous methods would be backwards-incompatible.
  2. Write their own HttpWebRequest equivalent. Unfortunately, this would take a lot of work and they'd lose all the interoperability with existing WebRequest-related code.
  3. Queue requests to the thread pool to avoid the worst-case scenario (blocking synchronous code on the UI thread). Unfortunately, this has the side effects of degrading scalability on ASP.NET, being dependent on a free thread pool thread, and incurring the worst-case scenario cost even for best-case scenarios.

In an ideal world (i.e., when we have infinite developer and tester time), I would prefer (2), but I understand why they chose (3).

On a side note, the code you posted shows a dangerous use of StartNew, which has actually caused problems due to its use of TaskScheduler.Current. This has been fixed in .NET Core - not sure when the fix will roll back into .NET Framework proper.

Upvotes: 4

VMAtm
VMAtm

Reputation: 28355

No, your assumptions are wrong.

  1. StartNew isn't equal to the Run method.
  2. This code is from HttpClientHandler, not the HttpClient, and you didn't examine the this.startRequest code from this class. The code you're inspecting is a prepare method, which starts a task in new thread pool, and inside call actual code to start an http request.
  3. HTTP-connection is created not on the .NET level of abstraction, and I'm sure that inside startRequest you'LL find some P/Invoke method, which will do actual work for:

    • DNS lookup
    • Socket connection
    • Sending the request
    • waiting for the answer
    • etc.
  4. As you can see, all above are logic which really should be called in async manner, because it is outside the .NET framework, and some operation can be very time-consuming. This is exactly logic that should be called asynchroniously, and during the waiting for it .NET thread is being released in ThreadPool to process other tasks.

Upvotes: 0

Related Questions