Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Value is not updating after async method resume

Looking at this code :

public class SharedData
{
    public int Value { get; set; }
}

void button1_Click(object sender, EventArgs e)
{
    AAA();
}

async Task BBB(SharedData data)
{
    await Task.Delay(TimeSpan.FromSeconds(1));
    MessageBox.Show(data.Value.ToString()); //<---- I always see 0 here,
    data.Value = data.Value + 1;
}

async Task<int> AAA()
{
    SharedData data = new SharedData();
    var task1 = BBB(data);
    var task2 = BBB(data);
    var task3 = BBB(data);

    await Task.WhenAll(task1, task2, task3);
    MessageBox.Show(data.Value.ToString());  //<--- this does show 3
    return data.Value;
}

This is a GUI (windows forms ) application , which means that there is only one thread executing each line of code.

All BBB(data) invocations are invoked very quick without waiting. Each BBB invocation get's into the BBB method , sees await which doesn't complete and returns ( to AAA).

Now , when one second (approx) is elapsed, all continuations are happening in the GUI thread.

Question

Continuations doesn't happen simultaneously because it's a GUI thread . So a previous statement of :

data.Value = data.Value + 1;

Must have occurred.

In other words ,

I know that all BBBs are invoked with the same initial value of data , but the continuations doesn't happens concurrently

The GUI thread must run eventually:

continuation #1

 MessageBox.Show(data.Value.ToString()); 
 data.Value = data.Value + 1; //So this basically should do 0-->1
....

continuation #2

 MessageBox.Show(data.Value.ToString()); // Why data.Value still "0" ??
 data.Value = data.Value + 1;  
....

continuation #3

 MessageBox.Show(data.Value.ToString()); // Why data.Value still "0" ??
 data.Value = data.Value + 1;

It looks like the continuations are scheduled not as a whole but as shared quanta ?

like image 477
Royi Namir Avatar asked Sep 14 '15 06:09

Royi Namir


1 Answers

That happens because you're using MessageBox.Show for debug prints and showing a modal message box blocks the code-flow but doesn't block the UI thread (otherwise you couldn't interact with the message box).

So, when the first continuation is "blocked" by showing a message box the next continuation can run and then the third. They are all "blocked" by showing the message boxes but the UI thread itself isn't.

That's why they all show 0 and only when you release the message boxes they can continue running and increase the variable.

You can see that if you print the value using Console.WriteLine before showing the message box:

async Task BBB(SharedData data)
{
    await Task.Delay(TimeSpan.FromSeconds(1));
    Console.WriteLine(data.Value);
    MessageBox.Show(data.Value.ToString());
    data.Value = data.Value + 1;
}

You'll notice that 0 is printed 3 times by all the continuations exactly after 1 second and not after you dismiss the message boxes.

Basically, the continuations run concurrently, but not in parallel using the same thread because of the MessageBox.Show in them.

If you use Console.WriteLine instead of MessageBox.Show you will see that the value is incremented one at a time:

async Task BBB(SharedData data)
{
    await Task.Delay(TimeSpan.FromSeconds(1));
    Console.WriteLine(data.Value);
    data.Value = data.Value + 1;
}

Output:

0
1
2
like image 99
i3arnon Avatar answered Oct 08 '22 19:10

i3arnon