Why does HttpClient appear to hang without .Result? [duplicate] - c#

This question already has answers here:
An async/await example that causes a deadlock
(5 answers)
Closed 3 years ago.
I have this code to call an API which returns a token. However, it will only return if I replace this line:
var response = await TokenClient.PostAsync(EnvironmentHelper.TokenUrl, formContent);
with this line:
var response = TokenClient.PostAsync(EnvironmentHelper.TokenUrl, formContent).Result;
Why?
public static async Task<Token> GetAPIToken()
{
if (DateTime.Now < Token.Expiry)
return Token;
TokenClient.DefaultRequestHeaders.Clear();
TokenClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
TokenClient.BaseAddress = new Uri(EnvironmentHelper.BaseUrl);
TokenClient.Timeout = TimeSpan.FromSeconds(3);
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", EnvironmentHelper.APITokenClientId),
new KeyValuePair<string, string>("client_secret", EnvironmentHelper.APITokenClientSecret)
});
try
{
// HANGS - this next line will only ever return if suffixed with '.Result'
var response = await TokenClient.PostAsync(EnvironmentHelper.TokenUrl, formContent);
var content = await response.Content.ReadAsStringAsync();
dynamic jsonContent = JsonConvert.DeserializeObject(content);
Token token = new Token();
token.AccessToken = jsonContent.access_token;
token.Type = jsonContent.token_type;
token.Expiry = DateTimeOffset.Now.AddSeconds(Convert.ToDouble(jsonContent.expires_in.ToString()) - 30);
token.Scope = Convert.ToString(jsonContent.scope).Split(' ');
Token = token;
}
catch (Exception ex)
{
var m = ex.Message;
}
return Token;
}

The top-most code where awaiting is not performed has this: var wm = new WebRepo().GetWeb(Id).Result
You're running into a deadlock since the synchronous code is blocking on async code. The best solution is to remove the blocking and go async all the way.
The calling code is a public static property with a getter, referenced in over 100 places. I'm not sure how best to convert the property to async friendly
There are a few approaches to async properties, depending on what the property's semantics are. If it re-evaluates each time it's called, then it's really a method in disguise and should become an async Task<T> method. If it is set once, then AsyncLazy<T> may be a better choice.
Is there a way to wrap one of the calls? It is a mammoth task to go and refactor that much code.
There's no wrapping that works for all scenarios. However, there are some techniques for mixing sync and async code that can work for specific scenarios.
Here's what I would consider, in order of precedence:
Make it async all the way. Refactor the code and make it better. (Requires lots of code changes, which may not be feasible at this time).
Make it sync all the way. Change GetAPIToken (and its callers) to be sync instead of async. (Requires all calling code to be made synchronous, which may not be possible).
Use the boolean argument hack to make GetAPIToken callable both synchronously and asynchronously. (Requires the ability to change GetAPIToken).
Use the thread pool hack to run GetAPIToken asynchronously on a thread pool thread and sycnhronously block another thread on it.

Related

Is there a way to force Task with HttpClient API response?

I have a task who want to call from the constructor class but it's really slow for executing. Is there a way to force this task?
private async Task GetExchange()
{
NewsStack.IsVisible = false;
SearchStack.IsVisible = false;
ExchangeStack.IsVisible = true;
try
{
var client = new HttpClient();
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri("https://coinlore-cryptocurrency.p.rapidapi.com/api/tickers/?start=0&limit=100"),
Headers =
{
{ "x-rapidapi-host", "coinlore-cryptocurrency.p.rapidapi.com" },
{ "x-rapidapi-key", "yourAPIkey" },
},
};
using (var response = await client.SendAsync(request))
{
var exchange = new Exchange();
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync();
var exchangeBody = JsonConvert.DeserializeObject<Exchange>(body);
exchange = exchangeBody;
this.exchangeBodyList = new List<SearchCrypto>();
foreach (var item in exchange.CryptoExchange)
{
this.exchangeBodyList.Add(new SearchCrypto()
{
Name = item.Name,
Symbol = item.Symbol
});
}
this.exchangeTest = exchange;
lstExchange.ItemsSource = exchangeBody.CryptoExchange;
}
dateTimeRefresh.Text = "Last Update: " + DateTime.Now.ToString("HH:mm:ss");
}
catch (Exception ex)
{
await DisplayAlert("Alert", "Please, check your internet connection.", "OK");
}
}
I call this task in constructor like that:
Task.Run(() => this.GetExchange()).Wait();
I'm not sure if there's a way to force it in another way.
Also I accepting tips or examples for code optimization.
In general, asynchronous work is a poor fit for constructors. Ideally, constructors should be short and fast and do almost nothing - setting some member variables, perhaps doing some argument validation, that's about it.
Instead of trying to cram I/O into a constructor, consider using a factory pattern. So you create a factory, which can then create an instance of the type you want using an asynchronous method like async Task<MyType> CreateAsync(). CreateAsync can then call GetExchange naturally (i.e., asynchronously) and pass exchangeBodyList and exchangeTest into the constructor.
What point are you trying to accomplish by forcing the API call to finish? Just like most things, the server will give a response when it's performed all it's operations, not before. The only way to force the result early is to close the connection and not wait for an answer. If you just want it to speed up and finish quicker, then you'll need to speed up the server side code and any DB calls.
Just like in any program, there's no way to force code to run faster. You can't make the computer run faster. You can force it to run a thread at a higher priority, but I'm pretty sure that's not going to make much speed difference and it's probably not the format you need the code to run in.
Speeding up code isn't really on topic here, unless you have an actual, specific error or issue you want to fix, but a general "speed up my code" doesn't work here. It might be on topic on Code Review, maybe, but not here.

Async JSON deserialisation in ASP.NET

I have a small MVC 5 application that calls a web service, and receives a JSON response. I deserialise the response into my own type and it gets passed on to the view and data is displayed by razor.
The controller handler:
public async Task<ActionResult> Search(string q)
{
var vm = new SearchResultViewModel(await _searchService.GetDataAsync(q));
return View(vm);
}
The search service method:
public async Task<ISearchResult> GetDataAsync(string q)
{
var fullRequest = new UriBuilder(RequestUri) {Query = "q=" + q};
var result = await _client.GetAsync(fullRequest.ToString()).ConfigureAwait(false);
if (result.IsSuccessStatusCode)
{
var jsonResponse = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
// How should I call this?
return JsonConvert.DeserializeObject<SearchResult>(jsonResponse);
}
return new SearchResult
}
My question: How should I call JsonConvert.DeserializeObject? It's an inherently CPU bound operation, so is it ok to call synchronously (and block the thread) since I can't return until it's done anyway? If there's a problem with deserialisation, a cancellation token couldn't be used.
If I should call asynchronously, should I use Task.Factory.StartNew() as suggested by intellisense, as a replacement for the deprecated JsonConvert.DeserializeObjectAsync()? This Channel 9 video suggests (at 58mins) that this isn't such a good idea. Perhaps another option, such as Task.Run()? Possibly a bad idea since it might cause SyncContext issues?
Any pointers gratefully received!
Your code is good as is. DeserializeObject will run inside a thread-pool thread since you are using ConfigureAwait(false).
Your overall method (GetDataAsync) would still be asynchronous since it will return to the caller on the first await.

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#.

Where to use concurrency when calling an API

Inside a c# project I'm making some calls to a web api, the thing is that I'm doing them within a loop in a method. Usually there are not so many but even though I was thinking of taking advantage of parallelism.
What I am trying so far is
public void DeployView(int itemId, string itemCode, int environmentTypeId)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(ConfigurationManager.AppSettings["ApiUrl"]);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var agents = _agentRepository.GetAgentsByitemId(itemId);
var tasks = agents.Select(async a =>
{
var viewPostRequest = new
{
AgentId = a.AgentId,
itemCode = itemCode,
EnvironmentId = environmentTypeId
};
var response = await client.PostAsJsonAsync("api/postView", viewPostRequest);
});
Task.WhenAll(tasks);
}
}
But wonder if that's the correct path, or should I try to parallel the whole DeployView (i.e. even before using the HttpClient)
Now that I see it posted, I reckon I can't just remove the variable response as well, just do the await without setting it to any variable
Thanks
Usually there is no need to parallelize the requests - one thread making async requests should be enough (even if you have hundreds of requests). Consider this code:
var tasks = agents.Select(a =>
{
var viewPostRequest = new
{
AgentId = a.AgentId,
itemCode = itemCode,
EnvironmentId = environmentTypeId
};
return client.PostAsJsonAsync("api/postView", viewPostRequest);
});
//now tasks is IEnumerable<Task<WebResponse>>
await Task.WhenAll(tasks);
//now all the responses are available
foreach(WebResponse response in tasks.Select(p=> p.Result))
{
//do something with the response
}
However, you can utilize parallelism when processing the responses. Instead of the above 'foreach' loop you may use:
Parallel.Foreach(tasks.Select(p=> p.Result), response => ProcessResponse(response));
But TMO, this is the best utilization of asynchronous and parallelism:
var tasks = agents.Select(async a =>
{
var viewPostRequest = new
{
AgentId = a.AgentId,
itemCode = itemCode,
EnvironmentId = environmentTypeId
};
var response = await client.PostAsJsonAsync("api/postView", viewPostRequest);
ProcessResponse(response);
});
await Task.WhenAll(tasks);
There is a major difference between the first and last examples:
In the first one, you have one thread launching async requests, waits (non blocking) for all of them to return, and only then processing them.
In the second example, you attach a continuation to each Task. That way, every response gets processed as soon as it arrives. Assuming the current TaskScheduler allows parallel (multithreaded) execution of Tasks, no response remains idle as in the first example.
*Edit - if you do decide to do it parallel, you can use just one instance of HttpClient - it's thread safe.
What you're introducing is concurrency, not parallelism. More on that here.
Your direction is good, though a few minor changes that I would make:
First, you should mark your method as async Task as you're using Task.WhenAll, which returns an awaitable, which you will need to asynchronously wait on. Next, You can simply return the operation from PostAsJsonAsync, instead of awaiting each call inside your Select. This will save a little bit of overhead as it won't generate the state-machine for the async call:
public async Task DeployViewAsync(int itemId, string itemCode, int environmentTypeId)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(ConfigurationManager.AppSettings["ApiUrl"]);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var agents = _agentRepository.GetAgentsByitemId(itemId);
var agentTasks = agents.Select(a =>
{
var viewPostRequest = new
{
AgentId = a.AgentId,
itemCode = itemCode,
EnvironmentId = environmentTypeId
};
return client.PostAsJsonAsync("api/postView", viewPostRequest);
});
await Task.WhenAll(agentTasks);
}
}
HttpClient is able to make concurrent requests (see #usr link for more), thus I don't see a reason to create a new instance each time inside your lambda. Note that if you consume DeployViewAsync multiple times, perhaps you'll want to keep your HttpClient around instead of allocating one each time, and dispose it once you no longer need its services.
HttpClient appears to be usable for concurrent requests. I have not verified this myself, this is just what I gather from searching. Therefore, you don't have to create a new client for each task that you are starting. You can do what is most convenient to you.
In general I strive to share as little (mutable) state as possible. Resource acquisitions should generally be pushed inwards towards their usage. I think it's better style to create a helper CreateHttpClient and create a new client for each request here. Consider making the Select body a new async method. Then, the HttpClient usage is completely hidden from DeployView.
Don't forget to await the WhenAll task and make the method async Task. (If you do not understand why that is necessary you've got some research about await to do.)

Async/Await in foreach with HTTPClient

I have a webservice that loads up some plugins (dlls) and calls their Process method. One of the plugins takes a list of members and ensures that they are all included in a MailChimp list.
Here is the code that adds the users to the MailChimp group.
private async Task AddMCUsers(List<Member> _memberList)
{
using (var http = new HttpClient())
{
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user:password");
http.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", creds);
string memberURI = string.Format(#"{0}lists/{1}/members", _baseURI, _memberGroupId);
var jss = new JavaScriptSerializer();
foreach (var user in _memberlist)
{
var _addStatus = "";
try
{
var content = jss.Serialize(new MCPost()
{
email_address = user.Email,
status = "subscribed",
merge_fields = new MCMergeFields()
{
FNAME = user.Firstname,
LNAME = user.Lastname
}
});
using(var result = await http.PostAsync(memberURI, new StringContent(content,Encoding.UTF8, "application/json")))
{
var resultText = await result.Content.ReadAsStringAsync();
if(result.IsSuccessStatusCode)
{
_addStatus = "Success";
var _returnedUser = jss.Deserialize<MCMember>(resultText);
//Store new user's id
user.ServiceId = _returnedUser.id;
}
else
{
_addStatus = "Fail";
}
}
}
catch {
_addStatus = "Error";
}
LogEvent("Add User - " + _addStatus, string.Format("Id: {0} - {1} {2} (Account: {3}) : {4}", user.Id, user.Firstname, user.Lastname, user.AccountId, user.Email));
}
}
}
In normal procedural code, this wouldn't be a problem. However, the only Post method available on the httpClient was PostAsync. Being fairly new to the async/await stuff, I'm not sure the ramifications on the rest of my code ... particularly as it relates to my attempt to reuse the httpClient instead of instantiating a new one for each http call.
I'm not sure what happens with await when its wrapped in a foreach like I have. Will I run into issues with reusing the httpClient to make repeated calls when running asynchronously?
My other question is, what is actually going to be returned. IOW, my understanding is that await returns a Task. However, here, I'm looping through the list and making multiple calls to await PostAsync. My method returns a Task. But which task gets returned? If my calling method needs to wait for completion before moving on, what does its call look like?
private void Process()
{
//Get List
var task = AddMCUsers(list);
task.Wait();
//Subsequent processing
}
I've read that you should use Async all the way. Does this mean my calling method should look more like this?
public async Task Process()
{
//Get list
...
await AddMCUsers(list);
//Other processing
}
Thanks to whatever help you can offer on this.
In normal procedural code, this wouldn't be a problem.
The whole point of async/await is to write asynchronous code in a way that looks practically identical to "normal" synchronous code.
Being fairly new to the async/await stuff, I'm not sure the ramifications on the rest of my code ... particularly as it relates to my attempt to reuse the httpClient instead of instantiating a new one for each http call.
HttpClient was intended to be reused; in fact, it can be used for any number of calls simultaneously.
I'm not sure what happens with await when its wrapped in a foreach like I have.
One way to think of it is that await "pauses" the method until its operation completes. When the operation completes, then the method continues executing. I have an async intro that goes into more detail.
Will I run into issues with reusing the httpClient to make repeated calls when running asynchronously?
No, that's fine.
IOW, my understanding is that await returns a Task.
await takes a Task. It "unwraps" that task and returns the result of the task (if any). If the task completed with an exception, then await raises that exception.
My method returns a Task. But which task gets returned?
The Task returned from an async method is created by the async state machine. You don't have to worry about it. See my intro for more details.
If my calling method needs to wait for completion before moving on, what does its call look like? ... I've read that you should use Async all the way. Does this mean my calling method should look more like this?
Yes, it should look like your second snippet:
public async Task ProcessAsync()
{
//Get list
...
await AddMCUsers(list);
//Other processing
}
The only thing I changed was the Async suffix, which is recommended by the Task-based Asynchronous Pattern.
in your code you should be fine with reusing the HttpClient. What async / await is allow the code to release the execution thread to prevent locking a cpu thread while waiting for the web response. It also releases control back to the caller. When releasing code back to the caller it means that if your Process function does not await your AddMCUsers, Process could finish before AddMCUsers (useful in fire and forget situations to not await a method).
What async/await do not do is affect the logical flow of an individual method. When you await an async web call the execution is paused and then resumed at the same point once the web call returns. There is also thread context tracking and the code resumes in the same context (ie. UI thread or background thread depending on the parent) by default, but this can be changed if needed.
At some point in your code you may want to have a method that blocks until your async code competes and that is where you will want your Task.Wait() call to block execution. If all you use is awaits then it is possible for your program to end before your task competes. See the code example below.
class Program
{
static void Main(string[] args)
{
Task waitForMe = Task.Run(() => waitAsync());
}
static async Task waitAsync()
{
await Task.Delay(5000);
}
}
in the sample with out a Task.Wait call to block the Main method the program will end before the 5 second wait is complete. Having a main method of the following will cause the program to wait for 5 seconds before exiting:
static void Main(string[] args)
{
Task waitForMe = Task.Run(() => waitAsync());
waitForMe.Wait();
}

Categories

Resources