The following C# program produces unexpected output. I would expect to see:
Value1: 25, Value2: 10
Value1: 10, Value2: 25
but instead I see
Value1: 0, Value2: 10
Value1: 10, Value2: 25
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
DoWork().Wait();
Console.ReadLine();
}
private async static Task DoWork()
{
SomeClass foo = new SomeClass()
{
MyValue = 25.0f
};
PrintTwoValues(foo.MyValue, await GetValue());
PrintTwoValues(await GetValue(), foo.MyValue);
}
static void PrintTwoValues(float value1, float value2)
{
Console.WriteLine("Value1: {0}, Value2: {1}", value1, value2);
}
static Task<float> GetValue()
{
return Task.Factory.StartNew(() =>
{
return 10.0f;
});
}
class SomeClass
{
private float myValue;
public float MyValue
{
get
{
return this.myValue;
}
set
{
this.myValue = value;
}
}
}
}
}
Can somebody explain to me why it is that using the "await" operator in the expression for the second argument to the PrintTwoValues
method seems to be affecting the value of the first argument?
My guess is that it must have something to do with the fact that the argument list is evaluated left-to-right. In the first call to PrintTwoValues
I'm guessing that the return value from SomeClass.MyValue
gets pushed onto the stack. Then execution continues into GetValue
which just starts the Task and exits. Then DoWork
exits and schedules a continuation that will call PrintTwoValues
but when that continuation runs the value that had originally gotten pushed on the stack is somehow lost and reverted back to its default value.
While there are simple ways to workaround this problem, like storing the arguments in temporary variables before passing them to the PrintTwoValues
method, I'm mostly just curious why this behavior is occurring.
Note: I'm using Visual Studio 2013, Update 5. I'm building a console application that is targeting .NET Framework 4.5 and running on Windows 10 Enterprise.
The await operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes. When the asynchronous operation completes, the await operator returns the result of the operation, if any.
The await expression causes async function execution to pause until a Promise is settled (that is, fulfilled or rejected), and to resume execution of the async function after fulfillment. When resumed, the value of the await expression is that of the fulfilled Promise .
The async keyword turns a method into an async method, which allows you to use the await keyword in its body. When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete.
The await operator doesn't block the thread that evaluates the async method. When the await operator suspends the enclosing async method, the control returns to the caller of the method.
I've tested the code both with the C#5 compiler and the C#6 compiler using respectively LinqPad 4 and LinqPad 5 and I could reproduce the issue.
This looks like a compiler bug of the C#5 compiler, because when I decomplied both versions with .NET Reflector 9, I got different code:
private async static Task DoWork()
{
float myValue;
SomeClass foo = new SomeClass {
MyValue = 25f
};
float introduced6 = await GetValue();
PrintTwoValues(myValue, introduced6);
float introduced7 = await GetValue();
PrintTwoValues(introduced7, foo.MyValue);
}
private async static Task DoWork()
{
SomeClass foo = new SomeClass {
MyValue = 25f
};
float myValue = foo.MyValue;
float num2 = await GetValue();
float asyncVariable1 = num2;
PrintTwoValues(myValue, asyncVariable1);
num2 = await GetValue();
float asyncVariable2 = num2;
PrintTwoValues(asyncVariable2, foo.MyValue);
}
Notice that, for C#5, the myValue
variable is declared before the declaration of of foo
and never initialized before the first call to PrintTwoValues
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With