3

I'm developing an MVC web application that allows me to manage my data asynchronously through a web service.

It is my understanding this allows the CPU threads that access the app pool for the server upon which this website is running to return to the app pool after making a request so that they can be used to service other requests without stalling the entire thread.

Assuming my understanding is correct (although it may be badly worded), I got to thinking about when I should await things. Consider the function below:

public async Task<ActionResult> Index()
{
    List<User> users = new List<User>();
    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri("http://localhost:41979");
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(
                       new MediaTypeWithQualityHeaderValue("application/json"));

        HttpResponseMessage response = await client.GetAsync("api/user/");

        if (response.IsSuccessStatusCode)
        {
            users = await response.Content.ReadAsAsync<List<User>>();
        }
    }

    return View(users);
}

All of my functions look similar except that they do different things with the data returned by the web service and I got to wondering, should I not await the return as well?

Something like:

return await View(users);

or

await return View(users);

I mean the website runs just fine so far except that I've had a bit of confusion to do with exactly what the web service should send back to the client website, but as I'm new to development involving a web service, I'm still wondering if I'm doing things properly and this has been eating at me for some time.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Ortund
  • 8,095
  • 18
  • 71
  • 139

4 Answers4

4

You can only await named or anonymous methods and lambda expressions which expose an asynchronous operation via Task, Task<T> or a custom awaiter. Since View by itself does nothing asynchronous, you can't await it.

What is actually the point of using await? Usually, you have some IO bound operations which is asynchronous by nature. By using async API's, you allow the thread to be non-blocked by returning to the thread-pool, making use of it to serve different requests. async does not change the nature of HTTP. It is still "request-response". When an async method yields control, it does not return a response to the client. It will only return once the action has completed.

View(object obj) returns a ViewResult, which in turn will transform your object into the desired output. ViewResult is not awaitable, as it doesn't expose any "promise" via an awaitable object. Thus, you can't asynchronously wait on it.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
1

I got to thinking about when I should await things

It's better to always await the result from asynch calls.

If you don't await, you fire & forget, you don't receive response on your end in both success and error cases.

Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • That's not exactly true. If you fire and forget and you have an error in the task you will deadlock. If you want to truely fire and forget then use .ConfigureAwait(false) on your task so that it does not try to return to the sync context. – Stephen Brickner Jul 23 '15 at 13:39
  • Both this answer and Stephen's comment contain wrong information. Doing a `.ConfigureAwait(false)` has 0 effect if you never await the object the `ConfigureAwait` returns. Also you should never do Fire and forget in a ASP.NET environment because background tasks can and will have their threads aborted when the AppDomain shuts down after it has finished serving a request and no new requests have come in before the app pool recycling timeout. – Scott Chamberlain Jul 23 '15 at 13:53
  • ... You must use [HostingEnvironment.QueueBackgroundWorkItem](http://blogs.msdn.com/b/webdev/archive/2014/06/04/queuebackgroundworkitem-to-reliably-schedule-and-run-long-background-process-in-asp-net.aspx) on 4.5.2 or some [3rd party library](http://hangfire.io/) if you are pre 4.5.2 to not have your tasks ended early. – Scott Chamberlain Jul 23 '15 at 13:54
  • @ScottChamberlain I'm reading these answers and comments and I just don't feel like I'm understanding any of it :/ – Ortund Jul 23 '15 at 14:02
  • @Scott Chamberlain: sorry, I don't get what you said. Can you elaborate or point me to a link on the internet? What do you think about this: http://stackoverflow.com/questions/22864367/fire-and-forget-approach – Khanh TO Jul 23 '15 at 14:06
  • See http://haacked.com/archive/2011/10/16/the-dangers-of-implementing-recurring-background-tasks-in-asp-net.aspx/ In a desktop App it is fine, but there are special considerations you need to do for ASP.net. Also read through [this article](http://blogs.msdn.com/b/webdev/archive/2014/06/04/queuebackgroundworkitem-to-reliably-schedule-and-run-long-background-process-in-asp-net.aspx), it even links to 11 Stack Overflow questions where people got it wrong. – Scott Chamberlain Jul 23 '15 at 14:09
  • @Scott Chamberlain: You said: `background tasks can and will have their threads aborted when the AppDomain shuts down after it has finished serving a request and no new requests have come in before the app pool recycling timeout.` I'm not sure I understand 100%, but if we don't await, would the task be swallowed unnoticed, just as in the link I pointed out? Anyway, it's not good to just fire and forget, I will update the answer – Khanh TO Jul 23 '15 at 14:25
  • See this answer of mine a from a few days a go for a better analogy, just replace the word "Timer" with "Task.Run" http://stackoverflow.com/questions/31492736/threading-timer-doesnt-callback/31493440#31493440. For ASP.NET your last sentence should read "If you don't await, you fire & forget, you don't receive response on your end in both success and error cases. ***In fact, the thing you fired and forgot about might not even get a chance to finish running before IIS shuts itself down due to not having any new incoming requests.***" – Scott Chamberlain Jul 23 '15 at 14:28
  • @Scott Chamberlain: looks like it's not the same case. With your Timer sample, it's a problem because the Timer is garbage-collected and the callback is not called but in this case, the request `has already fired` and we don't care if the task is garbage-collected. – Khanh TO Jul 23 '15 at 14:38
  • Its not garbage collection, IIS calls `AppDomain.Unload(` on processes that have not served a user request within the time limit specified in the Application Pool's [Idle timeout](http://i.stack.imgur.com/00HJs.png) – Scott Chamberlain Jul 23 '15 at 14:45
  • @Scott Chamberlain: `Task` here is Task object from asynchronous programming, not `background tasks` as you said – Khanh TO Jul 23 '15 at 14:45
  • I know that, it still does not matter. If you get `AppDomain.Unload(` called on you before your Task finishes your work will not get done. – Scott Chamberlain Jul 23 '15 at 14:46
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/84087/discussion-between-khanh-to-and-scott-chamberlain). – Khanh TO Jul 23 '15 at 14:47
0

You "await" when you need to unwrap the async task as a value. Other wise you can return a task and have it run at a later time if needed. Also note that .Wait() is not the same as await. Wait() will block until the task has finished (side note I know). Also check your code, you have a syntax error in your method signature:

public async <Task>ActionResult> Index()

Should be

public async Task<ActionResult> Index()
Stephen Brickner
  • 2,584
  • 1
  • 11
  • 19
0

I think it is a very good question and also very hard to answer it especially in the case of a web site.

I've had a bit of confusion to do with exactly what the web service should send back to the client website'

The most important thing to understand if you use async/await, then your action method code still serialized, I mean the next line will be executed only after the async operation finished. However there will be (at least) three threads:

  • To original web server worker thread in which the action method is called. Originally the MVC infrastructure got this thread from the pool, and dedicated this thread to serve the current request.
  • Optional thread(s) what are started by the async operation what can be called with await.
  • A continuation thread (also a worker from the pool) in which your action method is continuing after the await. Note this thread will have the same context (HttpContext, user culture etc) what was the original worker so it's transparent for you, but it will be an other thread freshly allocated from the pool.

For the first sight it has no sense: Why all those thread hocus-pocus in case if the operations in the action method are still serialized? I takes the same time... To understand this we must take a look to a desktop application say a WPF app.

To be short: There is a message loop and a dedicated thread which reads the UI (and other virtual) events from a queue and executes the event handlers in the context of this thread, like a button click event. In case you block this thread for 2 second the UI will be unresponsive for that time. So that's why we would like to do many things in async way in an other thread, and let the dedicated (UI) thread to fast return and process other messages from the queue. However this could be very inconvenient, because we sometime wanted to continue something after the async operation ended and we had the result. The C# feature async/await made this very convenient. This case the continuation thread is always the dedicated UI thread but in its (very) later round in its endless loop. To wrap it up:

In an event handler you can use async/await to still execute your operations in serialized, the continuation will be done always in the original dedicated UI thread but during the await this thread can loop, and process other messages.

Now back to the MVC action method: In this case your action method is the analogy of the event handler. Although it is still hard to understand the use of the thread complication: This case blocking the action method would not block anything (as it blocked the dedicated UI thread in WPF). Here are the facts:

  • Other clients (browsers) requests will be served by other threads. (so we are not blocking anything by blocking this thread (*) keep reading
  • This request will not be served faster even if we use async/await because the operations are serialized and we must wait (await) the result of the async operation. (Do not confuse async/await with parallel processing)

So the use of async/await and the transparent thread tricks is not so obvious as it was in the case of the desktop application.

Still: Pool is not an endless resource. Releasing ASAP back to the pool (by await) the worker thread what was originally dedicated to serve the request, and let continue in an other thread after completion the async operation may lead better utilization of the thread pool and may lead better performance of the web server under extreme stress.

g.pickardou
  • 32,346
  • 36
  • 123
  • 268