Reputation: 180
I have a synchronous method that needs to call multiple times the same http end point with different parameters, wait for all requests to return and process the combined results in a synchronous fashion. Here is the simplified implementation of the synchronous method:
public void Test(IEnumerable<int> configs){
var ratings = Task.WhenAll(configs.Select(x=>Rate(x)));
ratings.Wait();
// More work done once all Rate tasks are complete
var test = ratings.Result.Select(x=>....);
}
And the simplified implementation of the Rate method is as follows:
private async Task<JToken> Rate(int arg){
var content = CreateContent(arg);
var uri = "http://fake.com/foo/bar";
var requestUri = new Uri(uri);
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await client.PostAsync(requestUri, content))
{
return await response.Content.ReadAsAsync<JToken>();
}
}
}
I am able to verify that the http requests are getting posted asynchronously the way I want, however, I never get past ratings.Wait()
. The status of ratings
is always WaitingForActivation with Result
showing Not yet computed. I am unsure about what is happening here.
Is wrapping the client
and the response
in using
statements with await
causing them to be disposed of before the method can return? If I call .Result
in the Rate
method, I get past the ratings.Wait()
, but then http calls are made synchronously, which defeats the whole purpose.
EDIT:
As suggested below, I used Task.WaitAll, unfortunately with the same result. It looks like the individual Rate
tasks are not resolving although I can see that I am receiving a 200 Http Code as a response in fiddler.
public void Test(IEnumerable<int> configs){
var tasks = configs.Select(x => Rate(x)).ToArray();
Task.WaitAll(tasks);
var ratings = tasks.Select(x => x.Result);
// More work done once all Rate tasks are complete
var test = ratings.Result.Select(x=>....);
}
Upvotes: 0
Views: 2877
Reputation: 283773
You have a deadlock. The PostAsync
completion callbacks are sent to your thread's event queue, which will get processed only after your current function returns to the main event loop. Since you're not returning until the completion does its thing... deadlock.
You can avoid this by starting the Tasks on the thread pool. You can request completion callbacks on the thread pool via GetAwaiter().ConfigureAwait(false)
, but this has to be done on each Task, not just the composite Task, or the Task returned by an async
function. Better to launch the whole async
function on the thread pool using Task.Run
so that its subtasks automatically have affinity to the threadpool and not your UI thread.
Upvotes: 4
Reputation: 536
I don't have any experience with using Task.WhenAll
, but you should be able to simply await
for the task collection to be completed. Like this:
public async void Test(IEnumerable<int> configs){
var ratings = await Task.WhenAll(configs.Select(x=>Rate(x)));
// More work done once all Rate tasks are complete
var test = ratings.Select(x=>....);
}
I'm currently not able to try it out myself, unfortunately.
Upvotes: -1