Reusable HttpClient instance vs static variable (Heavily used in multiple threads)? - c#

I currently have an instance variable of a HttpClient which is used in an instance method to download an image. The method is always called in a Task.Run().
This method can be called thousands of times in a minute and doesn't crash or produce any errors. But I just wanted to know what benefits, if any, there would be if I switched to a static HttpClient, apart from perhaps being more thread safe.
Here is some code for context:
HttpClient client = new HttpClient(); // Make this static?
// Always called in a Task.Run(() => DownloadImage(Obj));
public async void DownloadImage(Object obj)
{
FormUrlEncodedContent formContent = GetFormContent(Obj);
HttpResponseMessage Result = await client.PostAsync("URL", formContent).ConfigureAwait(false);
byte[] Data = Result.Content.ReadAsByteArrayAsync().Result;
StaticClass.Images[Obj.ID] = ImageSource.FromStream(() => new MemoryStream(Data));
formContent.Dispose();
Result.Dispose();
}

No
Since you are not using async version and you are calling ReadAsByteArrayAsync().Result you will most likely end up in deadlocks.
Recommended
Following is the most recommended way,
static HttpClient client = new HttpClient ();
public async Task DownloadImage(Object obj)
{
using(FormUrlEncodedContent formContent = GetFormContent(Obj)) {
using(HttpResponseMessage Result = await
client.PostAsync("URL", formContent)
.ConfigureAwait(false)){
byte[] Data = await Result.Content.ReadAsByteArrayAsync();
StaticClass.Images[Obj.ID] = ImageSource.FromStream(
() => new MemoryStream(Data));
}
}
}
Also don't call Dispose, use using block, Dispose will not be executed if an exception is thrown, using block will properly Dispose even in case of exception.
As long as, all disposables are wrapped in using and async await are used properly, HttpClient will perform best with single static instance.

HttpClient is intented by microsoft to be used as a static object. Even if the time to instatiate it every time is trivial, because other issues could arise.
his link contains a simple implementation :
https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client .
Since you will be using the client in more more cases than simple getting an image I would suggest a class with static methods (async if you may) for get/post/put/delete where you would inserting method names and objects dynamically.

Related

SemaphoreSlim deadlocks on 9th thread

I have a test automation environment with multithreaded tests that use a shared HttpClient to test methods on our Web API. After the HttpClient has been initialized, it can be used by all of our tests running on multiple threads, since it is a thread safe object. However, keeping the initialization from happening more than once is a challenge. Furthermore, it includes the await keyword in it, so it cannot use any basic lock technology to ensure the initialization operation is atomic.
To make sure the initialization happens properly, I am using a SemaphoreSlim to create a mutex for initialization. To get access to the object, all tests have to call a function that uses the SemaphoreSlim to make sure it has been properly initialized by the first thread to request it.
I found the following implementation for using SemaphoreSlim on this web page.
public class TimedLock
{
private readonly SemaphoreSlim toLock;
public TimedLock()
{
toLock = new SemaphoreSlim(1, 1);
}
public LockReleaser Lock(TimeSpan timeout)
{
if (toLock.Wait(timeout))
{
return new LockReleaser(toLock);
}
throw new TimeoutException();
}
public struct LockReleaser : IDisposable
{
private readonly SemaphoreSlim toRelease;
public LockReleaser(SemaphoreSlim toRelease)
{
this.toRelease = toRelease;
}
public void Dispose()
{
toRelease.Release();
}
}
}
I use this class like so:
private static HttpClient _client;
private static TimedLock _timedLock = new();
protected async Task<HttpClient> GetClient()
{
using (_timedLock.Lock(TimeSpan.FromSeconds(600)))
{
if (_client != null)
{
return _client;
}
MyWebApplicationFactory<Startup> factory = new();
_client = factory.CreateClient();
Request myRequest = new Request()
{
//.....Authentication code
};
HttpResponseMessage result = await _client.PostAsJsonAsync("api/accounts/Authenticate", myRequest);
result.EnsureSuccessStatusCode();
AuthenticateResponse Response = await result.Content.ReadAsAsync<AuthenticateResponse>();
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Response.Token);
return _client;
}
}
It worked flawlessly until just recently, when I added a ninth thread to my code. I've had to dial that back to 8 threads, because whenever I allow a 9th thread to call the TimedLock.Lock method, the entire program deadlocks.
Does anyone know what might be going on, or how to work around this problem?
OK. I figured out my own problem, and it really was MY own problem, nobody else's.
If you compare my code above really closely to the source that I quoted as getting it from, you'll notice there's actually a difference. The original code implements the Lock function asynchronously using the WaitAsync function built into SemaphoreSlim:
public async Task<LockReleaser> Lock(TimeSpan timeout)
{
if(await toLock.WaitAsync(timeout))
{
return new LockReleaser(toLock);
}
throw new TimeoutException();
}
And of course, in my code that uses it, add the await keyword at the proper place to take care of the added Task object properly:
...
using (await _timedLock.Lock(TimeSpan.FromSeconds(6000)))
{
...
Yesterday I "discovered" that if I changed the toLock object to use WaitAsync, the problem magically went away, and I was SO proud of myself. But then just a few minutes ago, when I was copying and pasting the "original" code into my question, I realized that the "original" code actually included "my" fix!
I now remember looking at this a few months ago and wondering why they needed to make this an Async function. So in my superior wisdom, I tried it without the Async, saw that it worked fine, and continued on until I just recently started using enough threads to demonstrate why it is necessary!
So to keep from confusing people, I changed the code in my question to be the bad code that I originally changed it to be, and put the truly original good code above here in "my" answer, which actually should be credited to Richard Blewett, the author of the referenced article.
I can't say I completely understand why this fix actually works, so any further answers that can explain it better are more than welcome!

HttpClient and long running Windows Services - What is the potential for thread deadlock and how to circumvent it

Note: I am running on .NET Framework 4.6.2
Background
I have a long running Windows Services that, once a minute, queues up a series of business related tasks that are ran on their own threads that are each awaited on by the main thread. There can only be one set of business related tasks running at the same time, as to disallow for race conditions. At certain points, each business task makes a series of asynchronous calls, in parallel, off to an external API via an HttpClient in a singleton wrapper. This results in anywhere between 20-100 API calls per second being made via HttpClient.
The issue
About twice a week for the past month, a deadlock issue (I believe) has been cropping up. Whenever it does happen, I have been restarting the Windows Service frequently as we can't afford to have the service going down for more than 20 minutes at a time without it causing serious business impact. From what I can see, any one of the business tasks will try sending a set of API calls and further API calls made using the HttpClient will fail to ever return, resulting in the task running up against a fairly generous timeout on the cancellation token that is created for each business task. I can see that the requests are reaching the await HttpClientInstance.SendAsync(request, cts.Token).ConfigureAwait(false) line, but do not advance past it.
For a additional clarification here, once the first business task begins deadlocking with HttpClient, any new threads attempting to send API requests using the HttpClient end up timing out. New business threads are being queued up, but they cannot utilize the instance of HttpClient at all.
Is this a deadlocking situation? If so, how do I avoid it?
Relevant Code
HttpClientWrapper
public static class HttpClientWrapper
{
private static HttpClientHandler _httpClientHandler;
//legacy class that is extension of DelegatingHandler. I don't believe we are using any part of
//it outside of the inner handler. This could probably be cleaned up a little more to be fair
private static TimeoutHandler _timeoutHandler;
private static readonly Lazy<HttpClient> _httpClient =
new Lazy<HttpClient>(() => new HttpClient(_timeoutHandler));
public static HttpClient HttpClientInstance => _httpClient.Value;
public static async Task<Response> CallAPI(string url, HttpMethod httpMethod, CancellationTokenSource cts, string requestObj = "")
{
//class that contains fields for logging purposes
var response = new Response();
string accessToken;
var content = new StringContent(requestObj, Encoding.UTF8, "application/json");
var request = new HttpRequestMessage(httpMethod, new Uri(url));
if (!string.IsNullOrWhiteSpace(requestObj))
{
request.Content = content;
}
HttpResponseMessage resp = null;
try
{
resp = await HttpClientInstance.SendAsync(request, cts.Token).ConfigureAwait(false);
}
catch (Exception ex)
{
if ((ex.InnerException is OperationCanceledException || ex.InnerException is TaskCanceledException) && !cts.IsCancellationRequested)
throw new TimeoutException();
throw;
}
response.ReturnedJson = await resp.Content.ReadAsStringAsync();
// non-relevant post-call variables being set for logging...
return response;
}
//called on start up of the Windows Service
public static void SetProxyUse(bool useProxy)
{
if (useProxy || !ServerEnv.IsOnServer)
{
_httpClientHandler = new HttpClientHandler
{
UseProxy = true,
Proxy = new WebProxy {Address = /* in-house proxy */},
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
}
else
{
_httpClientHandler = new HttpClientHandler
{
UseProxy = false,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
}
_handler = new TimeoutHandler
{
DefaultTimeout = TimeSpan.FromSeconds(120),
InnerHandler = _httpClientHandler
};
}
}
Generalized function from a business class
For more context.
//Code for generating work parameters in each batch of work
...
foreach (var workBatch in batchesOfWork)
{
var tasks = workBatch.Select(async batch =>
workBatch.Result = await GetData(/* work related parms*/)
);
await Task.WhenAll(tasks);
}
...
GetData() function
//code for formating url
try
{
response = await HttpClientWrapper.CallAPI(formattedUrl, HttpMethod.Get, cts);
}
catch (TimeoutException)
{
//retry logic
}
...
//JSON deserialization, error handling, etc.....
Edit
I forgot to mention that this also set on start-up.
ServicePointManager
.FindServicePoint(/* base uri for the API that we are contacting*/)
.ConnectionLeaseTimeout = 60000; // 1 minute
ServicePointManager.DnsRefreshTimeout = 60000;
The above mentioned code example shows that a common instance of HttpClient is being used by all the running applications.
Microsoft documentation recommends that the HttpClient object be instantiated once per application, rather than per-use.
This recommendation is applicable for the requests within one application.
This is for the purpose of ensuring common connection settings for all requests made to specific destination API.
However, when there are multiple applications, then the recommended approach is to have one instance of HttpClient per application instance, in order to avoid the scenario of one application waiting for the other to finish.
Removing the static keyword for the HttpClientWrapper class and updating the code so that each application can have its own instance of HttpClient will resolve the reported problem.
More information:
https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=netcore-3.1
After taking #David Browne - Microsoft's advice in the comment section, I changed the default amount of connections from the default (2) to the API provider's rate limit for my organization (100) and that seems to have done the trick. It has been several days since I've installed the change to production, and it is humming along nicely.
Additionally, I slimmed down the HttpClientWrapper class I had to contain the CallAPI function and a default HttpClientHandler implementation with the proxy/decompression settings I have above. It doesn't override the default timer anymore, as my thought is is that I should just retry the API call if it takes more than the default 100 seconds.
To anyone stumbling upon this thread:
1) One HttpClient being used throughout the entirety of your application will be fine, no matter the amount of threads or API calls being done by it. Just make sure to increase the number of DefaultConnections via the ServicePointManager. You also DO NOT have to use the HttpClient in a using context. It will work just fine in a lazy singleton as I demonstrate above. Don't worry about disposing of the HttpClient in a long running service.
2) Use async-await throughout your application. It is worth the pay-off as it makes the application much more readable and allows your threads to be freed up as you are awaiting a response back from the API. This might seem obvious, but it isn't if you haven't used the async-await architecture in an application before.

Async/await, run blocking the UI without obvious reason

I have been reasearching once again the async tasks. No mater how i set the Tasks, my application suffers from UI freeze all the time. I have the following code for downloading the string from a webpage:
internal string DownloadString(string URL)
{
var result = LoadCompanyContracts(URL);
return result.Result;
}
internal async Task<string> LoadCompanyContracts(string URL)
{
Task<string> task2 = Task<string>.Factory.StartNew(() =>
{
for (int i = 0; i <= 10000000; i++) Console.WriteLine(i);
WebClient wc = new WebClient();
string tmp = wc.DownloadString(new Uri(URL));
return tmp;
});
return task2.Result;
}
When i execute this task and during the for loop the UI of my application is freezing. Even though i believe that this code should not freeze the UI i am not able to find a solution. I have tried many different options and really want to use tasks instead of threads or events with webclient async.
Info: I am using .net 4.5 for my project. The difference in my code is that these functions are inside a class library(don't know if it matters).
Is it possible to run this code without blocking the user interface with async await by calling the DownloadString function from my code? If not what are the alternatives(any good nuget packages)?
The async keyword doesn't make something run asynchronously, it enables you to use await to await an already asynchronous operation. You need to use
DownloadStringTaskAsync to truly download in an asynchronous manner:
internal async Task<string> LoadCompanyContracts(string URL)
{
....
using(var wc = new WebClient())
{
string tmp = await wc.DownloadStringTaskAsync(new Uri(URL));
return tmp;
}
}
await by itself returns execution in the original execution context (ie the UI thread). This may or may not be desirable, which is why library code typically uses ConfigureAwait(false); and lets the final user of the library to decide how to await:
string tmp = await wc.DownloadStringTaskAsync(new Uri(URL))
.ConfigureAwait(false);
Finally, there's no point in awaiting if you are going to call .Result from the top-level function. There is no point in using await at all if you don't want to do use the method's result in your code. LoadCompanyContracts could be just:
internal Task<string> LoadCompanyContracts(string URL)
{
....
using(var wc = new WebClient())
{
return wc.DownloadStringTaskAsync(new Uri(URL))
.ConfigureAwait(false);
}
}
Oops
Typically, you don't need to use await at all if you just return the result of an asynchronous operation. The method could just return wc.DownloadStringTaskAsync(..); BUT that would cause the method to return and dispose the WebClient before download finishes. Avoiding the using block isn't a solution either, as it will let an expensive object like WebClient alive longer than necessary.
That's why HttpClient is preferable to WebClient: a single instance supports multiple concurrent calls, which means you can have just one instance eg as a field and reuse it, eg:
HttpClient _myClient =new HttpClient();
internal Task<string> LoadCompanyContractsAsync(string URL)
{
....
return _myClient.GetStringAsync(new Uri(URL))
.ConfigureAwait(false);
}
}
You could get rid of your DownloadString since it doesn't do anything on top of LoadCompanyContracts. If it does use the result of LoadCompanyContracts, it should be rewritten as:
internal async Task<string> DownloadString(string URL)
{
var result = await LoadCompanyContracts(URL);
//Do something with the result
return result;
}
EDIT
The original answer used DownloadStringAsync which is a legacy method that raises an event when download completes. The correct method is DownloadStringTaskAsync
EDIT 2
Since we are talking about a UI, the code can be made asynchronous all the way to the top event handler by using the async void syntax for the handler, eg async void Button1_Click, eg:
async void LoadCustomers_Click(...)
{
var contracts=await LoaCompanyContracts(_companyUrls);
txtContracts>Text=contracts;
}
In this case we want to return to the original thread, so we don't use ConfigureAwait(false);

Async method runs fine, but does not change anything

I want to read a XML file from the Web with following method.
public static async void Load_WinPhone(string URL)
{
HttpClient client = new HttpClient();
var httpResponseMessage = await client.GetAsync(new Uri(URL));
if (httpResponseMessage.StatusCode == System.Net.HttpStatusCode.OK)
{
var xmlStream = await httpResponseMessage.Content.ReadAsStreamAsync();
XDocument Xdoc = XDocument.Load(xmlStream);
var query = from data in Xdoc.Descendants("article")
select new MyClass
{
Title = data.Element("title").Value
}
foreach (MyClass x in query)
{
AnotherClass.List.Add(x);
}
}
This Works, but after the method finished the AnotherClass.List is still empty.
I think it is because of the async, I tried this in the console without the async and it worked fine.
But now i want to to this on a Windows Phone 8.1 and the list stays empty.
Can someone explain me why or even have a workaround for this?
Yes, that's how await works - from the point of view of the caller, it's basically the same thing as a return. So when you call this method, it most likely returns on the first await - long before AnotherClass.List is modified.
The main problem you have is that your method is async void - you're throwing away all the information about the method's execution. Instead, you want to return Task - this allows you to await the method or bind a continuation to it.
Whenever you break the await chain, you also break the synchronicity of the code. Most of the time (especially in UI), you want to await all the way to the top - usually, the only thing that's async void is the event handlers, and even then it's only because event handlers must return void.
Overall, multi-threading and asynchronous code is a rather big topic - http://www.albahari.com/threading/ is a great start on understanding most of the fundamentals, as well as ways to handle it well in C#.

Why use HttpClient for Synchronous Connection

I am building a class library to interact with an API. I need to call the API and process the XML response. I can see the benefits of using HttpClient for Asynchronous connectivity, but what I am doing is purely synchronous, so I cannot see any significant benefit over using HttpWebRequest.
If anyone can shed any light I would greatly appreciate it. I am not one for using new technology for the sake of it.
but what i am doing is purely synchronous
You could use HttpClient for synchronous requests just fine:
using (var client = new HttpClient())
{
var response = client.GetAsync("http://google.com").Result;
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
// by calling .Result you are synchronously reading the result
string responseString = responseContent.ReadAsStringAsync().Result;
Console.WriteLine(responseString);
}
}
As far as why you should use HttpClient over WebRequest is concerned, well, HttpClient is the new kid on the block and could contain improvements over the old client.
I'd re-iterate Donny V. answer and Josh's
"The only reason I wouldn't use the async version is if I were trying
to support an older version of .NET that does not already have built
in async support."
(and upvote if I had the reputation.)
I can't remember the last time if ever, I was grateful of the fact HttpWebRequest threw exceptions for status codes >= 400. To get around these issues you need to catch the exceptions immediately, and map them to some non-exception response mechanisms in your code...boring, tedious and error prone in itself. Whether it be communicating with a database, or implementing a bespoke web proxy, its 'nearly' always desirable that the Http driver just tell your application code what was returned, and leave it up to you to decide how to behave.
Hence HttpClient is preferable.
For anyone coming across this now, .NET 5.0 has added a synchronous Send method to HttpClient. https://github.com/dotnet/runtime/pull/34948
The merits as to why where discussed at length here: https://github.com/dotnet/runtime/issues/32125
You can therefore use this instead of SendAsync. For example
public string GetValue()
{
var client = new HttpClient();
var webRequest = new HttpRequestMessage(HttpMethod.Post, "http://your-api.com")
{
Content = new StringContent("{ 'some': 'value' }", Encoding.UTF8, "application/json")
};
var response = client.Send(webRequest);
using var reader = new StreamReader(response.Content.ReadAsStream());
return reader.ReadToEnd();
}
This code is just a simplified example - it's not production ready.
public static class AsyncHelper
{
private static readonly TaskFactory _taskFactory = new
TaskFactory(CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
=> _taskFactory
.StartNew(func)
.Unwrap()
.GetAwaiter()
.GetResult();
public static void RunSync(Func<Task> func)
=> _taskFactory
.StartNew(func)
.Unwrap()
.GetAwaiter()
.GetResult();
}
Then
AsyncHelper.RunSync(() => DoAsyncStuff());
if you use that class pass your async method as parameter you can call the async methods from sync methods in a safe way.
it's explained here :
https://cpratt.co/async-tips-tricks/
If you're building a class library, then perhaps the users of your library would like to use your library asynchronously. I think that's the biggest reason right there.
You also don't know how your library is going to be used. Perhaps the users will be processing lots and lots of requests, and doing so asynchronously will help it perform faster and more efficient.
If you can do so simply, try not to put the burden on the users of your library trying to make the flow asynchronous when you can take care of it for them.
The only reason I wouldn't use the async version is if I were trying to support an older version of .NET that does not already have built in async support.
In my case the accepted answer did not work. I was calling the API from an MVC application which had no async actions.
This is how I managed to make it work:
private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public static T RunSync<T>(Func<Task<T>> func)
{
CultureInfo cultureUi = CultureInfo.CurrentUICulture;
CultureInfo culture = CultureInfo.CurrentCulture;
return _myTaskFactory.StartNew<Task<T>>(delegate
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = cultureUi;
return func();
}).Unwrap<T>().GetAwaiter().GetResult();
}
Then I called it like this:
Helper.RunSync(new Func<Task<ReturnTypeGoesHere>>(async () => await AsyncCallGoesHere(myparameter)));
In the current era, the shortest answer to this question is quite straightforward: Literally all other prior .NET options other than HttpClient are now deprecated/obsolete.

Categories

Resources