1

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?

JustLogin
  • 1,822
  • 5
  • 28
  • 50

3 Answers3

5

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);
}
Lee
  • 142,018
  • 20
  • 234
  • 287
  • Which variable contains responses in this solution? – JustLogin Aug 17 '16 at 11:25
  • 2
    `await Task.WhenAll(tasks)` will return `string[]`, however correct code would be `return await Task.WhenAll(tasks)` or you may do like `var result = await Task.WhenAll(tasks)`; `return result` – Mrinal Kamboj Aug 17 '16 at 11:28
  • Thanks. Such a simple construction, but works perfectly! – JustLogin Aug 17 '16 at 11:32
  • Is this a guarantee, `Task.WhenAll` returns responses array in correct order (for example, response[0] is for _allIps[0])? – JustLogin Aug 17 '16 at 12:06
  • 1
    @JustLogin - Yes, see the **Remarks** section of the [docs](https://msdn.microsoft.com/en-us/library/hh194766.aspx) for `Task.WhenAll` – Lee Aug 17 '16 at 12:09
1
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...

Xyz
  • 5,955
  • 5
  • 40
  • 58
smoksnes
  • 10,509
  • 4
  • 49
  • 74
1

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:

WaitAll vs WhenAll

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

Community
  • 1
  • 1
Mrinal Kamboj
  • 11,300
  • 5
  • 40
  • 74
  • You have already accepted the correct code, mine is the detailed explanation of its working, I can paste the same details, if you really care – Mrinal Kamboj Aug 17 '16 at 11:44