Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

263
Views
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

over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

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.

over 4 years ago · Santiago Trujillo Report

0

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.

over 4 years ago · Santiago Trujillo Report

0

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

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!