-3

I've been struggling for about some days now on checking where to do await and where not to.

I have a Repository class which fetches the data from database. using EntityFramework the code would be something like this:

public async Task<List<Object>> GetAsync()
{
  return await context.Set<Object>().ToListAsync();
}

and the consumer:

var data = await GetAsync();

and on top level I'm awaiting this method too. should I use await on only one of these methods? Is it a performance penalty on using resources and creates new thread each time you do await?

I have checked the questions listed in the comments and they do not reffer to the performance issues and just say that you can do it. I wanted the best practice and the reason why to / not to do so.

MRebati
  • 591
  • 14
  • 29
  • 3
    Possible duplicate of [How should we use async await?](https://stackoverflow.com/questions/56125028/how-should-we-use-async-await) – Fabio May 20 '19 at 06:08
  • 2
    Possible duplicate of [What is the purpose of "return await" in C#?](https://stackoverflow.com/questions/19098143/what-is-the-purpose-of-return-await-in-c) – György Kőszeg May 20 '19 at 06:11
  • Benchmark it, and choose. yes of course there will be a performance difference. How much is the difference that you will worry about? Well... we don't know that. However, unless you completely know what you are doing with returning async methods as tasks, just use async. – TheGeneral May 20 '19 at 06:59
  • remark: sorry, if *GetAsync()* is the general pattern used in your repositories it looks like you don't make sense to care about the performance penalties because you download the full content of the table(s) from DB ;( – vladimir May 20 '19 at 07:04
  • 2
    If the only thing you're doing is call another async method, in general you should not use await in that case. If you have to put that call into a `using` statement, or a try/catch-block, then keep using await, but in your first example you should probably take out async/await and just return the task directly, ie. `public Task> GetAsync() { return context.Set().ToListAsync(); }` – Lasse V. Karlsen May 20 '19 at 08:27
  • @vladimir it was just an example to describe the issue. and while using the specified repo for domain entities which are not generic implementation I believed we could use the async methods, can't we? – MRebati May 20 '19 at 08:43
  • @LasseVågsætherKarlsen why would it be better to take the Async out and just return the Task, would it be better to enable the `Async Await` across the call chain, what would be the benefit when you insist on returning `Task>` – Mrinal Kamboj May 20 '19 at 10:10
  • Adding `await` here does nothing other than add another state machine into the mix, which will return once it gets reawakened after the inner task completes. If your method is only calling another method, which returns a task, there is no point creating a task B to wait for task A, you can just use A directly. – Lasse V. Karlsen May 20 '19 at 10:12
  • 1
    To be fair, the "rule" is more than that though. If your method ends with a call to a method returning a task, and that is the only task involved in your whole method, and you do not need to do anything in your method after this task completes (like exiting a try/catch, exiting a using, no more code, none of this), then you don't need to make your method `async`, you can simply return the task instead and save some (slight) overhead. – Lasse V. Karlsen May 20 '19 at 10:14
  • 2
    [Here you go.](http://blog.stephencleary.com/2016/12/eliding-async-await.html) – Stephen Cleary May 20 '19 at 12:31
  • "...creates new thread each time you do await?" Certainly not. The keyword `await` does not create threads. – Theodor Zoulias May 20 '19 at 15:02
  • @LasseVågsætherKarlsen whiel going through the Stephen Cleary's article [Eliding Async Await](http://blog.stephencleary.com/2016/12/eliding-async-await.html), shall not skip Async Await as a general rule, this gains are minimal and negative impact like exception not returning with Task could be huge, though in this special case `QueryableExtension.ToListAsync` is anyway Async by default – Mrinal Kamboj May 20 '19 at 16:03
  • I haven't read through his whole post, but some of the things that change, like exception handling, while though entirely correct, also contains some of his opinions. For instance, a method that can throw an exception *before* it gets to the async part, do you want that exception as part of the setup call, or do you want to get the exception only when you await the task? There's no right or wrong answer here, you need to know what the differences are and how you want it to behave, but if you don't know how async/await behaves in the first place, does it matter that the behavior changes? – Lasse V. Karlsen May 20 '19 at 16:21

2 Answers2

1

I'd like to add to that. There are some async methods where there is no need to use async/await keywords. It is important to detect this kind of misuse because adding the async modifier comes at a price.

E.G. You don't need async/await keywords in your example.

public Task<List<Object>> GetAsync()
{
  return context.Set<Object>().ToListAsync();
}

And then:

var data = await GetAsync();

Will be just fine. In this case, you are returning the Task<List<Object>> and then you are awaiting that in the place you directly work with objects.

I recommend installing async await helper

AlexZholob
  • 317
  • 1
  • 16
  • 2
    By no means Async Await is misuse even in this context, its just that `ToListAsync` is automatically Async even if not awaited, check [Eliding Async Await](http://blog.stephencleary.com/2016/12/eliding-async-await.html), as a rule its recommended to not elide, since gains are quite minimal and negative impact could be high – Mrinal Kamboj May 20 '19 at 15:59
  • Oh, that's right. Thanks for the link, very useful information. – AlexZholob May 21 '19 at 13:59
0

Let me get the essence of your question first, confusion is related to where to use the Async in the complete chain of calls and where not and how to assess the performance impact of usage, as it may lead to creation of more threads. If the synopsis goes beyond this add details to the comments, i till try to answer them too.

Let's divide and tackle each of them one by one.

Where to use the Async in the chain of calls and where not ?

  • Here as you are using Entity Framework to access a database, I can safely assume you are using IO based Asynchronous processing, which is the most prominent use case for Async processing across languages and frameworks, use cases for CPU based Asynchronous processing are relatively limited (will explain them too)
  • Async is a Scalability feature especially for IO processing instead of performance feature, in simple words via Async processing you can ensure that a hosted server can cater to many times more calls for IO processing, since calls are not blocking and they just hand over the processing request over the network, while process thread goes back to the pool ready to serve another request, complete handing over process few milliseconds
  • When the processing is complete, software thread need to just receive them and pass back to the client, again few millisecond, mostly its around < 1 ms, if its a pure pass through no logic call for IO

What are Benefits

  • Imagine instead making Synchronous call for IO to a database, where each thread involve will just wait for result to arrive, which may go in few seconds, impact will be highly negative, in general based on thread pool size, you may server 25 - 50 request at most and they too will reply on number of cores available to process, will continuously spin wasting resources, while they are idle and waiting for the response
  • If you make synchronous call there's no way to serve 1000+ requests in same setup and I am extremely conservative Async can actually have huge Scalability impact, where for light weight calls it may serve millions requests with ease from a single hosted process

After the background, where to use the Async in complete chain

  • Everywhere, feasible from begin to end, from entry point to actual exit point making IO call, since that's the actual call relieving the pool thread, as it dispatch the call over network
  • Do Remember though, await at a given point doesn't allow further code to process ins same method, even if it relieve the thread, so its better that if there are multiple independent calls, they are aggregated using Task.WhenAll and the representative task is awaited, it will return when all of them finish success / error, what ever may be the state
  • If the Async is broken in between by using something like Task.Wait or Task.Result, it will not remain pure Async call and will block the calling thread pool thread

How can Async be further enhanced ?

  • In Pure library calls, where Async is initiated by the Thread pool and dispatching thread can be different from receiving one and call doesn't need to reenter same context, you shall use ConfigureAwait(false), which means it will not wait to re-enter the original context and is a performance boost
  • Like await it makes sense to use ConfigureAwait(false) across the chain, entry to the end. This is valid only for libraries which reply extensively on thread pools

Is there a Thread created

Variations

  • CPU based Asychronous processing, where you take things in background, since current thread needs to be responsive, mostly in case of Ui like WPF

Use cases

  • All kinds of systems especially non MS frameworks, like node js, have Async processing as underlying principle and database server cluster on receiving end is tuned to receive millions of calls and process them
  • B2C calls, its expected that each request is light weight with limited Payload

Edit 1:

Just in this specific case as listed here, ToListAsyncis by default Asynchronous, so you can skip async await in that case as listed in variopus comments, though do review Stepehen Cleay's article in general that may not be a very good strategy, since gains are minimal and negative impact for incorrect usage can be high

Mrinal Kamboj
  • 11,300
  • 5
  • 40
  • 74