Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET and async - how it works?

I know that is a common question, but I've read a kiloton of articles and feel confused. And now I think that it would be better not to read them at all )).

So, how ASP.NET works (only about threads):

  1. http request is served by thread from the thread pool.
  2. while request is processing this thread is busy, because request is processing inside exactly this thread.
  3. when request processing finishes, the thread returns to thread pool, server sends a response.

Is this described behaviour right ?

What really happens when I start new task inside ASP.NET MVC controller?

public ActionResult Index()
{
    var task1 = Task.Factory.StartNew(() => DoSomeHeavyWork());
    return View();
}

private static async Task DoSomeHeavyWork()
{
    await Task.Delay(5000);
}
  1. controller action starts to execute inside thread that processes current request - T1.
  2. the thread pool allocates another one thread (T2) for task1.
  3. task1 starts "immediately" inside T2.
  4. the View result returns "immediately".
  5. ASP.NET do some work, server sends a response, T1 returns to the thread pool, T2 is still alive.
  6. after some time when the DoSomeHeavyWork will be finished, the T2 thread will be returned to the thread pool.

Is it correct ?

Now lets look to async action

public async Task<ActionResult> Index()
{
    await DoSomeHeavyWork();
    return View();
}

, I understand the difference with previous code sample, but not the process, in this example the behaviour is the following:

  1. action starts to execute inside thread that processes current request - T1.
  2. DoSomeHeavyWork "immediately" returns a task, let's call it "task1" too.
  3. T1 returns to the thread pool.
  4. after the DoSomeHeavyWork finishes, Index action continue to execute.
  5. after Index action execution the server will send a response.

Please explain what happening between points 2 and 5, the questions are:

  1. is the DoSomeHeavyWork processed inside task1 or where (where it is "awaited") ? I think this a key question.
  2. which thread will continue to process the request after await - any new one from the thread pool, right ?
  3. request produces thread allocating from the thread pool, but response will not be sent until the DoSomeHeavyWorkAsync finished and it doesn't matter in which thread this method executes. In other words, according to single request and single concrete task (DoSomeHeavyWork) there is no benefits of using async. Is it correct ?
  4. if the previous statement is correct, then I don't understand how the async can improve performance for multiple requests with the same single task. I'll try to explain. Let's assume that the thread pool has 50 threads available to handle requests. Single request should be processed at least by one single thread from the thread pool, if the request starts another threads, then all of them will be taken from the thread pool, e.g. request takes one thread to process himself, starts 5 different tasks in parallel and wait all of them, thread pool will have 50 - 1 - 5 = 44 free threads to handle incoming requests - so this is a parallelism, we can improve performance for single request, but we reduce number of requests which can be processed. So according request processing in ASP.NET I suppose that only task that somehow starts IO completion thread can achieve a goal of async (TAP). But how IO completion thread calls back thread pool thread in this case ?
like image 580
SamousPrime Avatar asked May 22 '15 08:05

SamousPrime


People also ask

What is async in asp net?

The async keyword represents a hint that you can use to mark methods as task-based asynchronous methods. The combination of await, async, and the Task object makes it much easier for you to write asynchronous code in . NET 4.5. The new model for asynchronous methods is called the Task-based Asynchronous Pattern (TAP).

How does async method work?

An async method runs synchronously until it reaches its first await expression, at which point the method is suspended until the awaited task is complete. In the meantime, control returns to the caller of the method, as the example in the next section shows.

How does .NET async await work?

The async keyword turns a method into an async method, which allows you to use the await keyword in its body. When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. await can only be used inside an async method.

How does async work in .NET Core?

Asynchronous programming allows you to write programs that don't block on each statement or instruction, meaning the computer can move on to other tasks before waiting for previous tasks to finish. As a result, asynchronous programming enables you to build applications that are more scalable and responsive.


1 Answers

Is this described behaviour right ?

Yes.

Is it correct ?

Yes.

is the DoSomeHeavyWork processed inside task1 or where (where it is "awaited") ? I think this a key question.

From the current code, DoSomeHeavyWork will asynchronously wait for Task.Delay to complete. Yes, this will happen on the same thread allocated by the thread-pool, it won't spin any new threads. But there isn't a guarantee that it will be the same thread, though.

which thread will continue to process the request after await?

Because we're talking about ASP.NET, that will be an arbitrary thread-pool thread, with the HttpContext marshaled onto it. If this was WinForms or WPF app, you'd be hitting the UI thread again right after the await, given that you don't use ConfigureAwait(false).

request produces thread allocating from the thread pool, but response will not be sent until the DoSomeHeavyWorkAsync finished and it doesn't matter in which thread this method executes. In other words, according to single request and single concrete task (DoSomeHeavyWork) there is no benefits of using async. Is it correct ?

In this particular case, you won't see the benefits of async. async shines when you have concurrent requests hitting the server, and alot of them are doing IO bound work. When using async while hitting the database, for example, you gain freeing the thread-pool thread for the amount of time the query executes, allowing the same thread to process more requests in the meanwhile.

But how IO completion thread calls back thread pool thread in this case ?

You have to separate parallelism and concurrency. If you need computation power for doing CPU bound work in parallel, async isn't the tool that will make it happen. On the other hand, if you have lots of concurrent IO bound operations, like hitting a database for CRUD operations, you can benefit from the usage of async by freeing the thread while to IO operation is executing. That's the major key point for async.

The thread-pool has dedicated pool of IO completion threads, as well as worker threads, which you can view by invoking ThreadPool.GetAvailableThreads. When you use IO bound operations, the thread that retrieves the callbacks is usually an IO completion thread, and not a worker thread. They are both have different pools.

like image 123
Yuval Itzchakov Avatar answered Sep 28 '22 20:09

Yuval Itzchakov