Reputation: 1886
I have 2 methods: the first sends HTTP GET
request on one address and the second calls it multiple times (so, it sends request to many IPs). Both methods are async, so they don't block code execution while the requests are proccessing remotely. The problem is, due to my poor C# knowledge, I don't know how to send all the requests simultaneously, not one after another (which my code does). That's my code:
public static async Task<string> SendRequest(Uri uri)
{
using (var client = new HttpClient())
{
var resp = await client.GetStringAsync(uri).ConfigureAwait(false);
return resp;
}
}
public static async Task<string[]> SendToAllIps(string req)
{
string[] resp = new string[_allIps.Length];
for (int i = 0; i < _allIps.Length; i++)
{
resp[i] = await SendRequest(new Uri(_allIps[i] + req));
}
return resp;
}
How to make SendToAllIps
send requests without awaiting for previous task result? Also SendToAllIps
must return an array of responses when all the requests are finished. As far as I understand, this can be done with Task.WaitAll, but how to use it in this particular situation?
Upvotes: 1
Views: 164
Reputation: 10871
public static async Task<string[]> SendToAllIps(string req)
{
var tasks = new List<Task<string>>();
for (int i = 0; i < _allIps.Length; i++)
{
// Start task and assign the task itself to a collection.
var task = SendRequest(new Uri(_allIps[i] + req));
tasks.Add(task);
}
// await all the tasks.
string[] resp = await Task.WhenAll(tasks);
return resp;
}
The key here is to collect all the tasks in a collection and then await them all using await
Task.WhenAll
. Even though I think the solution from Lee is more elegant...
Upvotes: 1
Reputation: 11478
Answers provided above provide the correct way of doing it but doesn't provide rationale, let me explain what's wrong with your code:
Following line creates an issue:
resp[i] = await SendRequest(new Uri(_allIps[i] + req));
Why ?
As you are awaiting each individual request, it will stop the processing of the remaining requests, that's the behavior of the async-await
and it will be almost the synchronous processing of each SendRequest
when you wanted then to be concurrent.
Resolution:
SendRequest
being an async method returns as Task<string>
, you need add that to an IEnumerable<Task<string>>
and then you have option:
Task.WaitAll
or Task.WhenAll
Yours is Web API (Rest Application), which needs a Synchronization Context
, so you need Task.WhenAll
, which provides a Task as result to wait upon and integrate all the task results in an array, if you try using Task.WaitAll
it will lead to deadlock, as it is not able to search the Synchronization Context
, check the following:
You can use the Task.WaitAll
only for the Console application not the Web Application, Console App doesn't need any Synchronization Context
or UI Thread
Upvotes: 1
Reputation: 144206
You can use Task.WhenAll
to await a collection of tasks:
public static async Task<string[]> SendToAllIps(string req)
{
var tasks = _allIps.Select(ip => SendRequest(new Uri(ip + req)));
return await Task.WhenAll(tasks);
}
Upvotes: 5