Waiting for Async does not mean that some threads will run your code.
However, this will reduce the time during which your thread will wait idly to complete processes, thereby ending earlier.
Whenever a stream usually needs to wait for something to end, for example, waiting for a web page to load, the database request to complete, writing to disk ends, the asynchronous wait stream will not wait idly while data is being written / retrieved, but looked around if they can do other things instead, and return later after completing the expected task.
This is described with the cook analogy in this inverview with Eric Lippert . Search somewhere in the middle for asynchronous wait.
Eric Lippert compares asynchronous waiting with one (!) Cook who is supposed to make breakfast. After he starts toasting the bread, he can calmly wait until the bread is browned before putting the kettle on the tea, wait until the water boils, before putting the tea leaves in the kettle, etc.
An asynchronous cook, not waiting for the toasted bread, but put on the kettle, and while the water is heating, he puts the tea leaves in the kettle.
Whenever a cook has to wait for something, he looks around and sees if he can do something instead.
A thread in an async function will do something similar. Since a function is asynchronous, you know that there is an expectation in the function. In fact, if you forget to program the wait, your compiler will warn you.
When your thread encounters a wait, it goes up the call stack to find out if it can do something else until it sees the wait, get back on the call stack, etc. As soon as everyone is waiting, he gets off the call to the stack and starts waiting until the first expected process ends.
Upon completion of the expected process, the thread will continue to process applications after waiting until it sees the wait.
It is possible that another thread will continue processing statements that appear after waiting (you can see this in the debugger by checking the thread ID). However, this other stream has the context of the original stream, so it can act as if it were the original stream. No need for mutexes, semaphores, IsInvokeRequired (in winforms), etc. It seems to you that there is one thread.
Sometimes your cook must do something that takes some time without waiting for an expectation, such as slicing tomatoes. In this case, it would be wise to hire another cook and order him to make a cut. At the same time, your cook can continue the eggs that have just finished boiling and the necessary peeling.
In computer terms, this would be if you had big calculations, without waiting for other processes. Pay attention to the difference, for example, writing data to disk. After your stream has ordered that data should be written to disk, it will usually wait until data is written. This is not the case when you do big calculations.
You can hire an extra cook using Task.Run
async Task<TimeSpan> CalculateSunSet() {