John's answer, of course, is wonderful; I thought I would just add one more thing.
View the WinForms application with a single button in a form that runs code when the button is clicked.
What happens if you do not press a button? Nothing. The process exists, the code works, but it seems to be doing nothing. In fact, what he does is to process the messages in the user interface thread and determine that none of them are interesting, but it does not look like he is doing something interesting.
When you click a button, unexpectedly one of these messages is interesting, and the message pump knows that when it sees this click event, it must run some code. The way it is.
The asynchronous scenario in one thread is one and the same. The continuation - “what to do after the task is completed” - is actually the “event completed” event handler of the event. When the task completes, it “clicks the button” and completes the message in the message queue of the user interface thread. It doesn’t matter if it does this from the user interface thread or from the I / O completion thread or something else. When the user interface thread approaches processing this message, it causes a continuation. In the same way as when a user interface thread is processed by a click of a button, it invokes a click handler.
Eric Lippert
source share