Async operations on ASP.NET MVC.

How Requests Are Processed by the Thread Pool

On the web server, the .NET Framework maintains a pool of threads that are used to service ASP.NET requests. When a request arrives, a thread from the pool is dispatched to process that request. If the request is processed synchronously, the thread that processes the request is busy while the request is being processed, and that thread cannot service another request.

The number of threads in the thread pool is limited (the default maximum for .NET 4.5 is 5,000). In large applications with high concurrency of long-running requests, all available threads might be busy. This condition is known as thread starvation. When this condition is reached, the web server queues requests. If the request queue becomes full, the web server rejects requests with an HTTP 503 status (Server Too Busy).

A web application using synchronous methods to service high latency calls where the thread pool grows to the .NET 4.5 default maximum of 5, 000 threads would consume approximately 5 GB more memory than an application able the service the same requests using asynchronous methods and only 50 threads. When you’re doing asynchronous work, you’re not always using a thread. For example, when you make an asynchronous web service request, ASP.NET will not be using any threads between the async method call and the await. Using the thread pool to service requests with high latency can lead to a large memory footprint and poor utilization of the server hardware.

Processing Asynchronous Requests

In web applications that sees a large number of concurrent requests at start-up or has a bursty load (where concurrency increases suddenly), making these web service calls asynchronous will increase the responsiveness of your application. An asynchronous request takes the same amount of time to process as a synchronous request. For example, if a request makes a web service call that requires two seconds to complete, the request takes two seconds whether it is performed synchronously or asynchronously. However, during an asynchronous call, a thread is not blocked from responding to other requests while it waits for the first request to complete. Therefore, asynchronous requests prevent request queuing and thread pool growth when there are many concurrent requests that invoke long-running operations.

Choosing Synchronous or Asynchronous Action Methods

Use synchronous methods for the following conditions:

  1. The operations are simple or short-running.
  2. Simplicity is more important than efficiency.
  3. The operations are primarily CPU operations instead of operations that involve extensive disk or network overhead. Using asynchronous action methods on CPU-bound operations provides no benefits and results in more overhead.

Use asynchronous methods for the following conditions:

  1. You’re calling services that can be consumed through asynchronous methods, and you’re using .NET 4.5 or higher.
  2. The operations are network-bound or I/O-bound instead of CPU-bound.
  3. Parallelism is more important than simplicity of code.
  4. You want to provide a mechanism that lets users cancel a long-running request.
  5. When the benefit of switching threads out weights the cost of the context switch. In general, you should make a method asynchronous if the synchronous method waits on the ASP.NET request thread while doing no work. By making the call asynchronous, the ASP.NET request thread is not stalled doing no work while it waits for the web service request to complete.
  6. Testing shows that the blocking operations are a bottleneck in site performance and that IIS can service more requests by using asynchronous methods for these blocking calls.

Creating an Asynchronous Action Method

The following example uses the new async and await keywords (available in .NET 4.5 and Visual Studio 2012) to let the compiler be responsible for maintaining the complicated transformations necessary for asynchronous programming. The compiler lets you write code using the C#’s synchronous control flow constructs and the compiler automatically applies the transformations necessary to use callbacks in order to avoid blocking threads.

public async Task<ActionResult> RegulatorsAsync()
{
    var regulatorsService = new RegulatorsService();
    return View("Regulators", await regulatorsService.GetRegulatorsAsync());
}

Let’s examing the code:

  • The method is marked with the async keyword, which tells the compiler to generate callbacks for parts of the body and to automatically create a Task that is returned.
  • “Async” was appended to the method name. Appending “Async” is not required but is the convention when writing asynchronous methods.
  • The return type was changed from ActionResult to Task. The return type of Task represents ongoing work and provides callers of the method with a handle through which to wait for the asynchronous operation’s completion. In this case, the caller is the web service. Task represents ongoing work with a result of ActionResult.
  • The await keyword was applied to the web service call.
  • The asynchronous web service API was called (GetRegulatorsAsync).

Inside of the RegulatorsAsync method body another asynchronous method, GetRegulatorsAsync is called. GetRegulatorsAsync immediately returns a <List> that will complete when the data is available. Because you don’t want to do anything else until you have the regulators data, the code awaits the task (using the await keyword). You can use the await keyword only in methods annotated with the async keyword.

The await keyword does not block the thread until the task is complete. It signs up the rest of the method as a callback on the task, and immediately returns. When the awaited task eventually completes, it will invoke that callback and thus resume the execution of the method right where it left off.

 

Used  resource: Using Asynchronous Methods in ASP.NET MVC 4

Share this post:Tweet about this on TwitterShare on Facebook0Share on LinkedIn0Share on Google+0Share on Reddit0Email this to someoneDigg this