Question

I have been working through an article about asynchronous controller methods in ASP.NET MVC (http://visualstudiomagazine.com/articles/2013/07/23/async-actions-in-aspnet-mvc-4.aspx) and I think I may be missing the point.

Consider this method I wrote, which is very similar to an example from the article:

[HttpGet]
[AsyncTimeout(8000)]
[HandleError(ExceptionType = typeof(TimeoutException), View = "TimedOut")]
public async Task<ActionResult> Index(CancellationToken cancellationToken)
{
    WidgetPageViewModel model = new WidgetPageViewModel()
    {
        toAdd = new Widget()
    };
    model.all = await _repo.GetAllAsync(cancellationToken);
    return View(model);
}

As I understand things, this is how things will unfold at runtime:

  1. An ASP.NET thread will be created for an incoming HTTP request.

  2. This thread will (having presumably done some necessary preliminary work) enter my Index() method above.

  3. Execution will reach the "await" keyword and kick off a data acquisition process on another thread.

  4. The original "ASP.NET" thread will return to the code that called my handler method, with an instance of class Task as return value.

  5. The infrastructural code that called my handler method will continue operating on the original "ASP.NET" thread, until it reaches a point where it needs to use the actual ActionResult object (e.g. to render the page).

  6. The caller will then access this object using the Task.Result member, which will cause it (i.e. the "ASP.NET" thread) to wait for the thread created implicitly in step #3 above.

I am not seeing what this accomplishes compared to the same thing without await / async, except for two things I perceive as trifling:

  • The caller thread and the worker thread created by await can operate in parallel for some period of time (the "until" part of #5 above). My hunch is that period of time is pretty small. When the infrastructure calls into a controller method, I'm thinking it generally needs the actual ActionResult of the controller call before it can do much (if anything) more.

  • There is some helpful new infrastructure related to timeout and cancellation of long-running asynchronous controller operations.

The purpose of adding async controller methods is supposedly to free up those ASP.NET worker threads to actually answer HTTP requests. These threads are a finite resource. Unfortunately, I'm not seeing how the pattern suggested in the article actually serves to conserve these threads. And even if it does, and somehow offloads the burden of handling the request to some non-ASP.NET thread, what does that accomplish? Are threads that happen to be capable of handling an HTTP request that much different from threads in general?

Was it helpful?

Solution

ASP.Net that does not use the Task Parallel Library (TPL) is limited in the number of requests it can handle concurrently by the number of threads in a thread pool. ASP.Net that uses the TPL is limited by the CPU/memory/IO of the machine handling requests.

The Task Parallel Library (TPL) does not consume threads in the way you seem to think it does. Tasks are not threads, they are a wrapper around some unit of computation. The task scheduler is responsible for executing each task on any thread that is not busy. At runtime, awaiting a task does not block the thread, it merely parks the execution state so that it may proceed at a later time.

Normally, a single HTTP request would be handled by a single thread, completely removing that thread from the pool until a response is returned. With the TPL, you are not bound by this constraint. Any request that come in starts a continuation with each unit of computation required to calculate a response able to execute on any thread in the pool. With this model, you can handle many more concurrent requests than with standard ASP.Net.

Licensed under: CC-BY-SA with attribution
scroll top