Benefits of Using Async and Await Keywords

Benefits of using async and await keywords

Say you have a single border checkpoint. Each car can pass it one-by-one to have customs take a look at their car to see if they're not smuggling any Belgian chocolate.

Now assume that you are in line in your Volkswagen Beetle where you can barely fit in and before you is a 24-wheel monstertruck. You are now stuck behind this behemoth for a long time until customs are done searching through it all before they can move on to you who they basically just have to pat down to tell you you're good to go.

In order to combat this efficiency, our good friends at the border patrol have an idea and install a second checkpoint. Now they can pass in twice as many people and you can just take that one instead of waiting behind the monstertruck!

Problem solved, right? Not exactly. They forgot to create a second road that leads to that checkpoint so all traffic still has to go over the single lane, resulting in the truck still blocking the Beetle.

How does this relate to your code? Very easy: you're doing the same.

When you create a new Task you essentially create that second checkpoint. However when you now synchronously block it using .Wait(), you are forcing everyone to take that single road.

In the second example you use await which creates that second road and allows your car to be handled simultaneously with the truck.

What is the benefit of C# async/await if it still waits for the previous execution to complete?

The point of async/await is not that methods are executed more quickly. Rather, it's about what a thread is doing while those methods are waiting for a response from a database, the file system, an HTTP request, or some other I/O.

Without asynchronous execution the thread just waits. It is, in a sense, wasted, because during that time it is doing nothing. We don't have an unlimited supply of threads, so having threads sit and wait is wasteful.

Async/await simply allows threads to do other things. Instead of waiting, the thread can serve some other request. And then, when the database query is finished or the HTTP request receives a response, the next available thread picks up execution of that code.

So yes, the individual lines in your example still execute in sequence. They just execute more efficiently. If your application is receiving many requests, it can process those requests sooner because more threads are available to do work instead of blocking, just waiting for a response from some I/O operation.

I highly recommend this blog post: There Is No Thread. There is some confusion that async/await is about executing something on another thread. It is not about that at all. It's about ensuring that no thread is sitting and waiting when it could be doing something else.

How and when to use ‘async’ and ‘await’

When using async and await the compiler generates a state machine in the background.

Here's an example on which I hope I can explain some of the high-level details that are going on:

public async Task MyMethodAsync()
{
Task<int> longRunningTask = LongRunningOperationAsync();
// independent work which doesn't need the result of LongRunningOperationAsync can be done here

//and now we call await on the task
int result = await longRunningTask;
//use the result
Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation
{
await Task.Delay(1000); // 1 second delay
return 1;
}

OK, so what happens here:

  1. Task<int> longRunningTask = LongRunningOperationAsync(); starts executing LongRunningOperation

  2. Independent work is done on let's assume the Main Thread (Thread ID = 1) then await longRunningTask is reached.

    Now, if the longRunningTask hasn't finished and it is still running, MyMethodAsync() will return to its calling method, thus the main thread doesn't get blocked. When the longRunningTask is done then a thread from the ThreadPool (can be any thread) will return to MyMethodAsync() in its previous context and continue execution (in this case printing the result to the console).

A second case would be that the longRunningTask has already finished its execution and the result is available. When reaching the await longRunningTask we already have the result so the code will continue executing on the very same thread. (in this case printing result to console). Of course this is not the case for the above example, where there's a Task.Delay(1000) involved.

Why use async when I have to use await?

If I use await here, what's the point of the asynchronous method?

await does not block thread. MyMethod2 will run synchronously until it reaches await expression. Then MyMethod2 will be suspended until awaited task (MyMethod) is complete. While MyMethod is not completed control will return to caller of MyMethod2. That's the point of await - caller will continue doing it's job.

Doesn't it make the async useless that VS is telling me to call await?

async is just a flag which means 'somewhere in the method you have one or more await'.

Does that not defeat the purpose of offloading a task to a thread
without waiting for it to finish?

As described above, you don't have to wait for task to finish. Nothing is blocked here.

NOTE: To follow framework naming standards I suggest you to add Async suffix to asynchronous method names.

Am I getting the benefits of async and await in the following code

The basic rule of thumb is "Without an await, async will run synchronously".

You could easily get your GroupBy() to run properly async by exchanging Task.WaitAll() for await Task.WhenAll().

What exactly happens when you call an async method without the await keyword?

Then my periodic job and PutRecordBatchAsync are processed concurrently?

Using Task API you can ensure that they are executed concurrently (using Thread pool), but you need to understand the difference between in memory concurrent operations Vs IO based concurrency.

While In memory concurrency does benefit using Tasks, IO call once executed doesn't need thread at all, as it relies on hardware concurrency, if it ever use the thread, all that it would do it wait for the IO call to return, thus wasting the precious system resources and reducing system scalability

You case is that IO based concurrency, as you call a remote / network based API, how does async-await helps here ?

Well true Async operation will free up the thread context, on windows it would use IO completion port (queuing mechanism) to execute the Async call, while the calling thread is used to dispatch other similar calls, it would just need thread context on return of the IO call for serving the response and for that too, if its not a UI call, then use ConfigureAwait(false), so that any thread context can be used to deliver the response.

What if you don't use await with async ?

The call meant to be Asynchronous becomes Synchronous and would immediately impact the system scalability, as threads are now blocked, even worse for a long running IO operations. Have you seen how JavaScript frameworks always make a AJAX (Async) call to the server API, thus much more work is possible W/o blocking the Browser threads.

In general for In memory processing you would create certain number of Tasks and process them using Task.WaitAll or Parallel.ForEach for a collection, for Async processing, ideally recommendation is not to have a Task.Run anywhere, its preferred to have to Async from the entry point, like its possible in case of MVC, controllers can be async. Multiple calls are grouped together using Task.WhenAll representative Task and then awaited upon. even if you use Task.Run as in your code, then use async lambda to execute an asynchronous call

Summary :

Its mandatory to use await for asynchronous calls, else they async keyword is useless in that context and yes await will wait for the IO call to return before continuation is executed, though no thread is blocked in the process

Should I actually use async-await in inner methods?

Regarding the second implementation without async-await:

public Task<int> CalculateBaseScore(int index, string type)
{
var conn_string = "...";
var sql = "...";
var param = new { Index = index, Type = type };
using (var conn = new SqlConnection(conn_string))
{
var db_output_task = conn.ExecuteScalarAsync<int>(sql, param: param);
return db_output_task;
}
}

...will give you a performance boost of around 1 μsec per invocation. In other words it will save you a whole second of CPU time after invoking it 1,000,000 times. These are the good news.

The bad news are that all these 1,000,000 invocations will result to an ObjectDisposedException, because the SqlConnection will be disposed prematurely, immediately after the creation of the Task.

Does it worth it? I'd say no. Keeping it simple will save you more time in the long run.



Related Topics



Leave a reply



Submit