4

We have been using async/await extensively because we need to access third-party async APIs. We are not doing UI and rarely need to use ASP.NET, we mainly write console applications. So most of our code generally looks like (hugely simplified):

static void Main()
{
   handle.Wait();
}

static async Task handle()
{
   while(true)
   {
      var a = await GetSomeThing();
      var b = await GetSomeThingElse(a);
      await DoSomething(a,b);
   }
}

My limited understanding of await is that it frees the program to work on other tasks while the operation completes; but since each operation is entirely dependent on the one before there is nothing else that the program could process in the meantime. So what practical difference is there between the (recommended?) way above and the alternative:

static void Main()
{
   while(true)
   {
      var a = GetSomeThing().Result;
      var b = GetSomeThingElse(a).Result;
      DoSomething(a,b).Wait();
   }
}

Some respondents allude to doing 'other work' or not blocking threads when calling 'handle' many times. Forgive me if I was not clear. There are no other tasks that I am aware of. We are writing essentially single-threaded sequential code where each stage is entirely dependent on the stage before and where we only use async because the third-party APIs require us to do so.

I should also emphasise that I am not 'opposed' to using the async syntax, I am merely trying to understand what benefit it actually provides in our use-case.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Rich
  • 51
  • 4
  • 3
    Those "other tasks" include updating a UI (for desktop/mobile) or handling other requests (for web). Using `.Result` instead of `await` may lead to deadlocks. Your original code is fine – Hans Kesting Apr 12 '23 at 14:13
  • 2
    Closed literally while I Was trying to write an answer. – Emperor Eto Apr 12 '23 at 14:25
  • There is a major difference which is that using `Result` and `Wait` block the thread while `await` does not. The use cases for `Result` and `Wait` are extremely limited these days. Always use `await` whenever possible. Yes, in your precise example it's fine because your program is doing nothing else. In the real world, you do not want any thread blocked while waiting for a `Task` to complete if you can avoid it. In some cases the very task you're waiting for was scheduled to run on the thread you're blocking, and the whole program will freeze. – Emperor Eto Apr 12 '23 at 14:26
  • What dotnet version are we talking about? `async Task Main()` is possible since a long time, now. – Fildor Apr 12 '23 at 14:26
  • Thanks, no it does not. I have read extensively (much of it by Stephen Cleary) and almost everything that I can find seems to provide examples from within a UI context or an ASP context - which is why I emphasise that this is a *console application* in which almost always `task 2 depends on the result of task 1`; `task 3 depends on the result of task 2` etc. – Rich Apr 12 '23 at 14:26
  • 1
    Although your Tasks may run _sequentially_ they (theoretically) could run asynchronously (which is **not the same as concurrently**). I'd advise to follow best practices anyway since that is probably going to cause the least problems. – Fildor Apr 12 '23 at 14:30
  • 1
    @TheodorZoulias Oooops. I did not realize the Q had been reopened. – Fildor Apr 12 '23 at 14:57
  • @Fildor agreed, we do use `async Task Main` for new work - but we have plenty of code which pre-dates this. – Rich Apr 12 '23 at 14:57
  • 1
    Literally the first time I've seen a good but hastily-closed question get reopened. Thanks @TheodorZoulias – Emperor Eto Apr 12 '23 at 16:37
  • 1
    @JustAnswertheQuestion you are welcome, but I am not going to be soft with your answer! – Theodor Zoulias Apr 12 '23 at 19:28

2 Answers2

1

"I should also emphasise that I am not 'opposed' to using the async syntax, I am merely trying to understand what benefit it actually provides in our use-case."

I would guess it is not so much beneficial regarding performance. I base this statement on "We are writing essentially single-threaded sequential code where each stage is entirely dependent on the stage before". That basically means that you cannot benefit from tasks executing concurrently.

Under certain circumstances it may even be detremental to performance if unneccessary state machines are being built and run. But that is a big fat IF. Most of the time I would expect this to have negligible effect.

But it would benefit the readability when forced to consume async APIs.

Although your Tasks may run sequentially they (theoretically) could run asynchronously (which is not the same as concurrently, always). That's why I would generally advise to follow best practices anyway since that is probably going to cause the least problems and be best readable.

EDIT: As you see, I am making not many statements about performance and efficiency (regarding memory for example).

There are just too many factors here: The nature of the 3rd party API calls ( IO vs CPU bound), what the compiler makes of your code (lowering, then IL ...), which makes it nearly impossible for me to write something like "it will be [bad|good] for performance". It might, it might not. "It" referring to usage of async/await vs .Result/ .Wait().

What I would expect, though, is that following best practices would give the compiler the best starting point for doing its optimization magic.

Fildor
  • 14,510
  • 4
  • 35
  • 67
  • *"I would guess it is not so much beneficial regarding performance."* -- Do you expect async/await to be slightly beneficial performance-wise, or completely neutral? And what about efficiency? Do you expect async/await to consume fewer resources (like memory) than the `.Result`? – Theodor Zoulias Apr 12 '23 at 15:28
  • @TheodorZoulias That are really interesting questions and also (for me) really hard to tell. The latest compilers are just so smart, it might actually have benefits. Or maybe it's slightly detremental ... It always depends on to what the code is lowered and ILed. That's why my answer baseline says not to set the focus on that _in this case_. To tell with certainty, I'd have to run the horses. It of course also depends on the nature of the 3rd party api calls. I/O-Bound calls may have different effects than CPU-Bound calls ... – Fildor Apr 12 '23 at 15:57
  • 3
    I asked because the OP in the question asks about the *"practical difference"*, and my understanding is that they are interested about performance and efficiency, not about readability and aesthetics. I would assume that they could make their own judgement about the readability of their code, and they wouldn't ask the community for opinions about that. So I am puzzled that the OP has accepted your answer, which provides so little information about efficiency and performance. – Theodor Zoulias Apr 12 '23 at 16:23
0

The difference between the two are that the the first one is async whereas the other one is not. By choosing the sync version (2nd) you hide the fact that it could have been async and therefore prevent someone from making use of async tasks in the future. There's only a few scenarios where choosing the sync over the async can be relevant and in the majority of cases, it is not.

Ask yourself this question : Do I really need the little performance boost of not using the async/await synatx ? If you do, maybe C# isn't the right language for your usecase.

Hervé
  • 760
  • 4
  • 9
  • 2
    _"Do I really need the little performance boost"_ - I doubt there is any but I we would have to fire up benchmarkdotnet to see. – Fildor Apr 12 '23 at 15:02