C# async/await usage

Hello everybody,

today I want to describe in simple words what C# does for .Net cases in IIS.

Before I continue, I want to point the following. As usually IIS has 500 threads. It means that one IIS server can simultaneously server 500 customers. Keeping this idea in mind

take a look at the following code:

[HttpGet]
public IEnumerable<string> GetSync()
{
    string val1 = "value" +  LongRunningMethod().ToString();
    string val2 = "value2";
    return new[] { val1, val2 };
}
 
private int LongRunningMethod()
{
    Thread.Sleep(10000);
    return 10;
}

and imagine that you have 600 customers simultaneously. For 500 lucky users of your web site it will mean that their requests will be server, but for other 100 it will mean... It will mean that they will nead to come at another more lucky time. How to fight with such situations? Prior to async/await pattern in C# following options were applicable:

  1. Create some separate threads that executed LongRunningMethod and overcome limitation of 500 threads in thread pool of IIS
  2. Create your own thread pool, again with flexible amount of threads ( it is almost like option 1, just with another entity )
  3. Increase number of threads in IIS via configuration ( aka task for admins )
  4. Use IAsyncResult interface with BeginSomething/EndSomething approach ( I've tested it and it works pretty fine, just a bit cumbersome in comparsion with async/await 
  5. Event based asynchronous pattern 

This list is not exhaustive because you can use combination of all 5 points as well as some other staff that I wasn't able to mention.

With async/await introduction it become very easy to overcome 500 threads limitation of IIS.

Take a look at method GetAsync:

[HttpGet]
 public async Task<IEnumerable<string>> GetAsync()
 {
     string val1 = "value" + await Task.Run( ()=> LongRunningMethod().ToString());
     string val2 = "value2";
     return new[] { val1, val2 };
 }

The biggest changes at this code are the following: 

  1. Added keyworkd async
  2. Before LongRunningMethod added keyword await.

Those two changes will cause significant changes. First of all let's consider case with one thread, and later scale it to before mentioned case with 600 simultaneous users.

For clarity also suppose that we have two threads instead of 500 and one user. Let's name those threads Thread a, Thread b.

User initiates action that causes execution of method GetAsync. Thread a become executor of method. Thread a initiated thread, located some space in memory for variable "value" and then Thread a sees keyword await. Thread a will execute method LongRunningMethod and leave it and will not wait for . After Thread a will leave method GetAsync IIS will have two threads ready to start their activity on some other staff. Imagine that user initiated some kind of activity and made Thread a busy, and while Thread a was busy, method LongRunningMethod finished. What will happen next? Thread b will pick up functionality and continue execution of method GetAsync. Now let's scale this scenario to 600 users. All 500 threads will be either busy with some practical functionality and non of them will waste time in waiting for result of LongRunningMethod function.

Summary

Keywords async and await allow you to not care about how to make your code asyncronous. Just add those two words with pair Task.Run to your code and you'll get scalability out of the box. 

No Comments

Add a Comment