I am actually reading some topics about the Task Parallel Library and the asynchronous programming with async and await. The book \"C# 5.0 in a Nutshell\" states that when await
Just for the sake of simplicity, I'm going to replace your example with one that's slightly simpler, but has all of the same meaningful properties:
async Task DisplayPrimeCounts()
{
for (int i = 0; i < 10; i++)
{
var value = await SomeExpensiveComputation(i);
Console.WriteLine(value);
}
Console.WriteLine("Done!");
}
The ordering is all maintained because of the definition of your code. Let's imagine stepping through it.
i
is initialized.SomeExpensiveComputation
is called. It should return a Task
very quickly, but the work that it'd doing will keep going on in the background.SomeExpensiveComputation
finishes, we store the result in value
.value
is printed to the console.As far as how the C# compiler actually accomplishes step 5, it does so by creating a state machine. Basically every time there is an await
there's a label indicating where it left off, and at the start of the method (or after it's resumed after any continuation fires) it checks the current state, and does a goto
to the spot where it left off. It also needs to hoist all local variables into fields of a new class so that the state of those local variables is maintained.
Now this transformation isn't actually done in C# code, it's done in IL, but this is sort of the morale equivalent of the code I showed above in a state machine. Note that this isn't valid C# (you cannot goto
into a a for
loop like this, but that restriction doesn't apply to the IL code that is actually used. There are also going to be differences between this and what C# actually does, but is should give you a basic idea of what's going on here:
internal class Foo
{
public int i;
public long value;
private int state = 0;
private Task task;
int result0;
public Task Bar()
{
var tcs = new TaskCompletionSource
Note that I've ignored task cancellation for the sake of this example, I've ignored the whole concept of capturing the current synchronization context, there's a bit more going on with error handling, etc. Don't consider this a complete implementation.