2

Can anyone explain to me how this makes sense? It was an answer in another post that I asked (Does await new Task(async () => Actually halt execution until finished?) and I'm not really understanding part of the comment...

An asynchronous wait -- await -- waits for a task to complete. That's why its called "await".

It is asynchronous because if the task is not complete then await returns to the caller and does other work on this thread until the task is complete, at which time the work that follows the await is scheduled to execute.

Make sure you understand that await is an operator on tasks. It is not a calling convention on calls. Any expression of awaitable type can be awaited; don't fall into the trap of believing that await makes a call asynchronous. The called method is already asynchronous; the task that is returned is the thing that is awaited.

How does it "wait for the task to complete" but at the same time "returns to the caller and does other work"?

Thanks!

FOLLOW-UP:

I've seen the responses, so what should happen in the following pseudo-code example? Which tasks in which order would actually happen?

await new Task(async () =>
{
    //Do stuff 1
    Thread.Sleep(10000);
}

EventLog.Write("Do Stuff 2");

await new Task(async () =>
{
    //Do stuff 3
    Thread.Sleep(10000);
}

Would "Do Stuff 1" execute, then wait, then "Do Stuff 2" would happen, then "Do Stuff 3"? Or would "Do Stuff 1" execute and then immediately "Do Stuff 2" followed by "Do Stuff 3"?

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
Nick Posey
  • 109
  • 3
  • 9
  • Possible duplicate of [Does await new Task(async () => Actually halt execution until finished?](https://stackoverflow.com/questions/47293580/does-await-new-taskasync-actually-halt-execution-until-finished) – Taylor Wood Nov 14 '17 at 21:24
  • It "logically" waits, meaning that the next line will only execute after the task completes. – SLaks Nov 14 '17 at 21:27
  • 2
    Method is broken up in parts. Before and after `await` is encountered. All code before `await` is executed synchronously. All code after `await` is scheduled as a `Continuation`. So it returns to the caller immediately, then when the `await` is done, the rest of the code gets called, either on `ThreadPool` thread or the captured `SynchronizationContext` – JohanP Nov 14 '17 at 21:29
  • Should be noticed that in windows GUI application continuation will executes after message loop is in idle state. – Fabio Nov 14 '17 at 21:34
  • I have updated my answer based on your example. – LeopardSkinPillBoxHat Nov 14 '17 at 21:53
  • TBH This description sounds wrong. If you have an `await`, that thread will wait for the task to finish before continuing. If you are `awaiting` then it cannot `returns to the caller and does other work` so as I said this description just sounds wrong. If you *don't* use `await` then a task will return to the caller and allow other work. – Worthy7 Nov 15 '17 at 01:19
  • @Worthy7 - that is not true. If you `await` a method, it returns a `Task` to the caller representing the state of that task. When the task completes, a continuation will be scheduled which will run the rest of the code after the `await` call. The example that the OP gave doesn't necessarily illustrate this fact, however. – LeopardSkinPillBoxHat Nov 15 '17 at 05:31
  • So what exactly isn't true about what I said – Worthy7 Nov 15 '17 at 05:33
  • @Worthy7 - It is true that a thread won't proceed past the `await` until the task is complete. However, using `await` allows us to return to the caller prematurely, which frees up the thread to do other stuff (e.g. process the message queue in a UI application, or process another request in a web application). For a UI application this ensures that the interface remains responsive. This is of course all possible without using `await`, but in many situations it is much cleaner doing it this way. – LeopardSkinPillBoxHat Nov 15 '17 at 21:20
  • @LeopardSkinPillBoxHat That doesn't make sense. You said "using await allows us to return to the caller prematurely," but won't it "return to the caller prematurely" regardless as long as it is an Async method? I cannot speak for UI's, but in a web app are you saying that `await` will end the thread and tell the main web server thread to start a new thread from just after the `await`, once the awaiting `Task/Thread` has completed? I thought that the caller thread would literally sit there in limbo, waiting – Worthy7 Nov 16 '17 at 03:42
  • @Worthy7 - Marking a method as `async` doesn't automatically make a method `async`. It simply allows us to yield back to the caller by using `await` at various points throughout the method. Using `await` in a method doesn't "end the thread". It kicks off the task that you're awaiting, then returns a `Task` back to the caller of the "in progress" task. If your call stack consists of multiple `async` methods, each at an `await` point, you'll eventually yield back to the controller, at which point the framework can then process another request. – LeopardSkinPillBoxHat Nov 16 '17 at 04:01
  • @Worthy7- Eventually the method you're awaiting will return, and the continuation will be queued up. I'm not 100% sure of the mechanics here, but I think at this point ASP.Net will allocate a thread from the thread pool to process the continuation. The idea is not to block unnecessarily, but use that time waiting for I/O to do something useful. You said "I thought that the caller thread would literally sit there in limbo, waiting". In that case, how would that be any different to calling a method synchronously? The purpose of `async` is to avoid that. – LeopardSkinPillBoxHat Nov 16 '17 at 04:03
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/159107/discussion-between-worthy7-and-leopardskinpillboxhat). – Worthy7 Nov 16 '17 at 04:06

1 Answers1

3

Not all work requires the CPU, or a thread.

Imagine you are making a HTTP request to a web service. Or streaming a file from disk or a network location. In these cases, the request is being handled by another hardware component such as a network adapter or disk controller.

What if there's latency on the network, and it's going to take a long time to get the response back? You could sit there blocking, waiting for the response. Or you could use that time to do something else.

This is where async and await are really useful. When you await a method which is doing I/O (e.g. disk or network), it's basically saying saying "please let me know when this task is complete". Then you will return the caller so you can do other work (maybe CPU bound, maybe I/O bound).

When the task that you awaited is finished, the rest of the method after the await statement is then run. This is called a continuation - the local state is captured at the time of the await statement, and it simply continues where it left off.

A good analogy I have heard is making breakfast. Let's say you are having a cup of coffee and toast. Boiling the kettle takes 2 minutes. You could stand there and wait for the kettle to boil, then make your coffee and toast. Or you could kick off the boiling task, go and make your toast, and then when you hear the whistling kettle, your "making coffee" task continues from where it left off. Which is more efficient?


In your example, you would get the following, in this order:

  • Wait 10 seconds (Do stuff 1)
  • Write to event log (Do stuff 2)
  • Wait 10 seconds (Do stuff 3)

We will not continue past each await call until the Task that you're awaiting is complete. In this case, you're awaiting an async Task, so it's actually a Task<Task> (because the async delegate itself uses a Task). But I think the outer await unwraps both Task and when the outermost Task is complete the continuation is scheduled.

LeopardSkinPillBoxHat
  • 28,915
  • 15
  • 75
  • 111