1

Sorry for the beginers question, I read a lot of post here and on the web and there is something fondemental I can't understand.

As I understood, the usage of async actions in WebAPI is mainly for scalability reasons, so any incoming request will be diverted to a worker instead to a thread and by that, more requests could be served.

My project consists on several huge actions that read/insert/update from DB by EF6 many times. the action looks like that:

    public async Task<HttpResponseMessage> Get(int id)
    {
     PolicyModel response = await policyRepository.GetPolicyAsync(id);
     return Request.CreateResponse<PolicyModel>(HttpStatusCode.OK,response);
    }

and GetPolicyAsync(int id) looks like that:

    public async Task<PolicyModel> GetPolicyAsync(int id)
    {
      PolicyModel response = new PolicyModel();
      User currentUser = await Repositories.User.GetUserDetailsAsync(id);

      if(currentUser.IsNew)
      {
         IEnumerable<Delivery> deliveries = await Repositories.Delivery.GetAvailableDeliveries();
         if(deliveries == null || deliveries.Count() == 0
         {
           throw new Exception("no deliveries available");
         }
         response.Deliveries = deliveries;
         Ienumerable<Line> lines = await Repositores.Delivery.GetActiveLinesAsync();
         lines.AsParallel().ForAll(line => {
         await Repositories.Delivery.AssignLineAsync(line,currentUser);
    }
    ...
      return response;   
    }

I didn't write the entire code but it goes quite a bit and it is also broken into several methods but that is the spirit of it

now the question i have is: is it a good practice to use so many awaiters in one method? I saw that it is more difficult to debug, is it hard to maintain the thread context and for the sake of worker assignment, shouldn't I just use Task.Factory.StartNew() or maybe call a simple Task.Delay() so that the request will immidiatelly be diverted to a worker? I know that it is not a good practice (async all the way) so maybe just one async method at the end/begining of the GetpolicyAsync(int id) method

EDIT:

as I understood the mechanics of the async methods in .net, for every async method, the compiler is looking for a free thread and let it deal withthe method, the thread is looking for a free worker and assign the method to it and then report back to the compiler that it is free. so if we have 10 threads and for every thread there are 10 workers, the program can deals with 100 concurrent async methods. so back to web developement, the IIS assign x thread to each app pool, 10 for instance. that means that the async WebAPI method can handle 100 requests but if there is another async method inside, the amount of requests that can be dealth with are 50 and so on, am I right? and as I understood, I must call an async method in order to make the WebAPI method truely async and now, since it is a bad practice to use Task.Factory.StartNew(), I must at least use Task.Delay()

what I really want to gain is the scalability of the async WebAPI methods and the context awareness of synced methods

in all the examples I've seen so far, they only show a very simple code but in real life, methods are far more complex

Thanks

armageddon
  • 125
  • 1
  • 10

2 Answers2

2

There's nothing wrong with having many awaits in a single method. If the method becomes too complicated for you you can split it up into several methods:

public async Task<PolicyModel> GetPolicyAsync(int id)
{
    PolicyModel response = new PolicyModel();
    User currentUser = await Repositories.User.GetUserDetailsAsync(id);

    if(currentUser.IsNew)
    {
        await HandleDeliveriesAsync(await Repositories.Delivery.GetAvailableDeliveries());
    }
    ...
    return response;   
}

public async Task HandleDeliveriesAsync(IEnumerable<Delivery> deliveries)
{
    if(deliveries == null || deliveries.Count() == 0
    {
        throw new Exception("no deliveries available");
    }
    response.Deliveries = deliveries;
    Ienumerable<Line> lines = await Repositores.Delivery.GetActiveLinesAsync();
    lines.AsParallel().ForAll(line => {
    await Repositories.Delivery.AssignLineAsync(line,currentUser);   
}

Don't use Task.Factory.StartNew or Task.Delay as it just offloads the same work to a different thread. It doesn't add any value in most cases and actually harms in ASP.Net.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • To add to this, if you crack open the Web API framework source, you can see that the whole request routing process has several methods with multiple awaits in them. – moarboilerplate Mar 30 '15 at 16:04
  • thank you so much @i3arnon for not sending me to an artical, I've updated my question to better explain my gap in my knowledge – armageddon Mar 30 '15 at 18:05
  • @armageddon `async` method don't need a background thread. You can look at another of my answers for more info: http://stackoverflow.com/a/28237602/885318 – i3arnon Mar 30 '15 at 18:43
0

After much search, I came across this artical: https://msdn.microsoft.com/en-us/magazine/dn802603.aspx

it explains what @i3arnon said in the comment. there are no workers at all, the threads are doing all the work.
In a nutshell, the thread that handles a web request reach to an opporation that is designed to be done asyncronically on the driver stack, it creates a request and pass it to the driver.
The driver mark it as pending and reports "done" to the thread which goes back to the thread pool to get another assignment.
The actual job is not being done by threads but by the driver that borrowing cpu time from all threads and leave them free to attend to their businesses.
When the opporation is done, the driver notifies it and an available thread comes to continue...

so, from that I learn that I should look into each and every async method and check that it actually does an opporation that uses the drivers asyncronically.

Point to think: the scallability of your website is dependent on the amount of opporations that are truly being done asyncronically, if you don't have any of them, then your website won't scale.

moderators, I'm a real newbe, if I got it all wrong, please correct me Thanks!

armageddon
  • 125
  • 1
  • 10