dbarnes
dbarnes

Reputation: 1833

differences in async functions in c#

I was reading about the async pattern in C# in depth, and I decided to take Jon's advice and decompile the async code to actually see what was going on. And I came across two scenarios that confused me a bit as to why they were different.

Consider these two functions:

public static async Task<string> GetValue(){
    int count = 0;
    count++;
    string g = "asdf";
    var r = Task.FromResult<string> ("hello");
    return g;
}

And

public static async Task<string> GetValue(){
    int count = 0;
    count++;
    string g = "asdf";
    var r = await Task.FromResult<string> ("hello");
    return g;
}

Now I'd expect the code to ouput the same IL being that they are both async functions so there needs to be a state machine. But to my surprise the C# compiler does create the statemachine in both cases follows all the same code as one would expect, except in the first code block it doesn't actually save any information in the machine. Where in the second it does store all the variables.

Is there a reason that the compiler decides to not save the variables in the state machine and expose two different code paths based on the await keword?

Upvotes: 0

Views: 126

Answers (3)

user4003407
user4003407

Reputation: 22122

Only variable, that visible by await expression need to be captured:

public static async Task Method() {
    int a=3;
    Console.WriteLine(a);
    {
        int b=3;
        Console.WriteLine(b);
    }
    await Task.FromResult(true);
}

For example, in code above a captured, b not captured. So, if you does not use await, nothing need to be captured.

Upvotes: 1

Greg
Greg

Reputation: 11480

I believe the disparity stems from the lack of the await operator. Without the await the compiler is treating your code as synchronous code. Which has no reason to utilize the state machine.

So in essence it creates the state machine as it expects asynchronous code, but you do not actually implement any code that is async. Which keeps it as an empty container.

The setup code initializes the state machine used to represent the asynchronous method and then kicks it off using a call to the secondary MoveNext method on the state machine. This state machine type holds state for the asynchronous method, allowing that state to be persisted across asynchronous await points, if necessary.

The State Machine holds your data in your second example because of the await. This triggers the State Machine to persist the data through multiple await points. Since the other example doesn't contain await, it remains empty. It only created the State Machine because you decorated it as async though it really isn't.

A small excerpt from an article on the matter, hopefully this helps.

Upvotes: 1

Henk Holterman
Henk Holterman

Reputation: 273244

The first one should have given you a warning about not really being async ("... will run synchronously") because there is no await in there.

So there is a state machine because you marked it async and it could be called with await.

But there is no information to keep because there is no await inside the method that would use it.

Upvotes: 2

Related Questions