203

In all the examples I can find of usages of HttpClient, it is used for one off calls. But what if I have a persistent client situation, where several requests can be made concurrently? Basically, is it safe to call client.PostAsync on 2 threads at once against the same instance of HttpClient.

I am not really looking for experimental results here. As a working example could simply be a fluke (and a persistent one at that), and a failing example can be a misconfiguration issue. Ideally I'm looking for some authoritative answer to the question of concurrency handling in HttpClient.

Jim Aho
  • 9,932
  • 15
  • 56
  • 87
Alex K
  • 10,835
  • 8
  • 29
  • 34
  • 2
    Also read this question for more info on how to properly use `HttpClient` and dispose of it: http://stackoverflow.com/questions/15705092/do-httpclient-and-httpclienthandler-have-to-be-disposed – Mani Gandham Jul 03 '16 at 19:43

3 Answers3

203

According to Microsoft Docs, since .NET 4.5 The following instance methods are thread safe (thanks @ischell):

CancelPendingRequests
DeleteAsync
GetAsync
GetByteArrayAsync
GetStreamAsync
GetStringAsync
PostAsync
PutAsync
SendAsync
PatchAsync
Martin Schneider
  • 14,263
  • 7
  • 55
  • 58
Marcel N.
  • 13,726
  • 5
  • 47
  • 72
  • 3
    Yeah, I wasn't sure about that one, as it appears to be a standard warning on everything on MSDN (and I remember reading some MSDN blogs about how sometime that warning is wrong, as it is applied blindly to everything). – Alex K Jun 24 '12 at 14:28
  • 1
    @AlexK: Well, I wouldn't believe anything I hear. Take a look at this one: http://msdn.microsoft.com/en-us/library/dd287191.aspx. So, I would generally trust MSDN. – Marcel N. Jun 24 '12 at 14:30
  • Well, the blogs are also on MSDN, a conundrum :) At any rate, I guess I'll err on the side of caution, and do client per request strategy. – Alex K Jun 24 '12 at 14:35
  • 2
    On the other hand, `HttpClient` has a [CancelPendingRequests](http://msdn.microsoft.com/en-us/library/system.net.http.httpclient.cancelpendingrequests(v=vs.110).aspx) which "Cancel[s] all pending requests on this instance." - that seems to suggest that it can have multiple requests in flight. – friism Jul 09 '12 at 02:49
  • 3
    This is wrong; in the remarks section of the MSDN page you linked, it says that GetAsync, PostAsync, etc. are all thread safe. – ischell Jan 03 '13 at 20:47
  • 4
    @ischell: I can assure you that the paragraph in question **was not** there at the time this issue was discussed. – Marcel N. Jan 03 '13 at 22:05
  • FYI it still says instance methods are not thread-safe at the bottom of the MSDN page. – Matt Oct 15 '13 at 20:45
  • @Matt: That's always been there and is a general note about thread safety for all .NET types. See the Remarks section up above in the MSDN article. – Marcel N. Oct 15 '13 at 21:45
  • 8
    So Microsoft have designed HttpClient to be reusable but then the class has instance data for headers: client.DefaultRequestHeaders.Accept.Add(...); – cwills Jun 11 '14 at 08:12
  • 2
    I'm not sure if HttpClient is thread-safe. There are 2 scenarios here: 1. Calling client.GetAsync(), waiting for it to return a Task object, then calling it again (on any thread) - should be OK - there is no concurrency with our calls. The HttpClient handles the request in the background and completes each task once the request is done, or 2. Calling client.GetAsync() repeatedly on multiple threads without waiting for the Task object to be returned each time. This is concurrency and I doubt this is supported out of the box. Async methods are not guaranteed to provide thread safety (case #2). – Alec Bryte Jun 02 '15 at 15:58
  • 13
    In late, but I wanted to comment on @cwills. DefaultRequestHeaders are just that, defaults. If you want different headers on a per-request-basis, you can create new StringContent(), set additional headers on that, then use the overload that takes URI and HttpContent. – Ryan Anderson Nov 21 '17 at 16:10
  • 2
    HttpClient also has a default maximum connections per server (`HttpClientHandler.MaxConnectionsPerServer`) of just 2. Make sure you increase that, or your multi-threaded requests will just block. – PerpetualStudent Jun 15 '18 at 12:27
  • 2
    Also, to set different Uri's and headers on the threaded requests, you can use an `HttpRequestMessage` and pass that to `SendAsync` for example. – PerpetualStudent Jun 15 '18 at 12:29
  • 1
    Does anyone know if this singleton pattern for HttpClient is safe to use server-side on an application that will have multiple users? – Chucky Feb 25 '19 at 10:11
107

Here is another article from Henrik F. Nielsen about HttpClient where he says:

"The default HttpClient is the simplest way in which you can start sending requests. A single HttpClient can be used to send as many HTTP requests as you want concurrently so in many scenarios you can just create one HttpClient and then use that for all your requests."

Community
  • 1
  • 1
muruge
  • 4,083
  • 3
  • 38
  • 45
  • 14
    What about if the username and password can change in between threads? that's what i can't seem to find anyone talking about – Nicholas DiPiazza Nov 05 '16 at 00:46
  • 1
    @NicholasDiPiazza: how often does it change? If there's a known set of user/password pairs then you can create a pool of HttpClient instances. – Marcel N. Jun 23 '17 at 11:57
  • yep that's what I ended up doing – Nicholas DiPiazza Jun 23 '17 at 17:41
  • 4
    Note that reusing the same HttpClient for all your requests might result in stale DNS issues: https://github.com/dotnet/corefx/issues/11224. – Ohad Schneider Jul 06 '17 at 21:52
  • 1
    @OhadSchneider If believe that issue is limited to .net core. You can fix the issue with .net 4 by injecting a custom HttpClientHandler into the HttpClient constructor then setting the "ConnectionLeaseTimeout". However, if no requests are sent to the endpoint for 100 seconds the connection will refresh on its own. protected override Task SendAsync(HttpRequestMessage request,CancellationToken cancellationToken) { var sp = ServicePointManager.FindServicePoint(request.RequestUri); sp.ConnectionLeaseTimeout = 100 * 1000; } – Timothy Gonzalez Oct 04 '17 at 14:13
  • What's the point? Is an HttpClient particularly expensive to where you shouldn't create a new one for each request? – xr280xr Apr 07 '21 at 04:08
  • @xr280xr simple answer: yes, disposing it will quickly exhaust your TCP sockets under load. Long answer: it's complex, and no one really knows the right way to use it. https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/ https://stackoverflow.com/a/15708633/825588 https://www.nimaara.com/beware-of-the-net-httpclient/ https://learn.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/#problem-description – Johann Apr 21 '21 at 23:03
  • Also https://stackoverflow.com/a/35045301/825588 and https://stackoverflow.com/a/19003704/825588 – Johann Apr 21 '21 at 23:13
  • @NicholasDiPiazza User name and password belong to the request header, not the underlying HTTP client. From my experience, creating HTTP client instances will quickly exhaust your server TCP connections pool. I learned this the hard way watching our servers on Azure burn down with no explanation because of this (we only realized the issue after looking at the server connections pool) – GETah Sep 30 '22 at 04:55
  • @GETah right. in many situations with low load, you can just kind of ignore this. but when you have a high load batch or web process that is used 1000's of times over an hour, then you need to be smart about when to open a connection, and when to wait until the pool has one free. – Nicholas DiPiazza Sep 30 '22 at 13:11
23

Found one MSDN forum post by Henrik F. Nielsen (one of HttpClient's principal Architects).

Quick summary:

  • If you have requests that are related (or won't step on eachother) then using the same HttpClient makes a lot of sense.

  • In genral I would recommend reusing HttpClient instances as much as possible.

xr280xr
  • 12,621
  • 7
  • 81
  • 125
Alex K
  • 10,835
  • 8
  • 29
  • 34