13

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?

3
  • 1
    Execution will reach the "await" keyword and kick off a data acquisition process on another thread -- Not necessarily. async doesn't require another thread... It's a continuation. It can be accomplished by reordering instructions on the same thread. Commented Mar 18, 2015 at 16:00
  • More info here: msdn.microsoft.com/en-us/library/hh191443.aspx Commented Mar 18, 2015 at 16:23
  • "Execution will reach the await keyword and kick off a data acquisition process on another thread." you got this backwards: the await is when it comes back. Try and split it up so you save the result from GetAllAsync() in a variable and await that instead and see if it becomes more clear. Commented Nov 8, 2019 at 15:25

1 Answer 1

9

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.

3
  • So when does the ASP.NET thread get returned to the pool? When the "await" keyword is hit? Commented Mar 18, 2015 at 18:15
  • async/await + TPL allow the compiler to divide your code into independent computational units which are executed by the task scheduler. A thread is conceptually returned to the pool when the task scheduler has no more tasks that can be executed and the thread finishes the work given to it. Commented Mar 18, 2015 at 18:43
  • This is conceptually correct and probably a useful answer... however the reality is just bit more complicated due to thread agility.
    – John Wu
    Commented Nov 9, 2019 at 0:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.