异步方法恢复后,值不会更新

时间:2015-09-14 06:55:59

标签: c# .net winforms async-await

查看此代码:

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;
}

这是一个GUI(Windows窗体)应用程序,这意味着每行代码只有一个线程。

无需等待即可快速调用所有BBB(data)次调用。 每个BBB调用进入BBB method,看到await未完成并返回(返回AAA)。

现在,当经过一秒钟(大约)时,所有延续都在GUI线程中发生

问题

Continuations不会同时发生,因为它是一个GUI线程。 所以先前声明:

data.Value = data.Value + 1;

必定已经发生。

换句话说,

我知道所有BBB都使用相同的初始值data进行调用,但延续不会同时发生

GUI线程最终必须运行:

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;

看起来这些延续不是作为一个整体而是作为共享量子安排的?

1 个答案:

答案 0 :(得分:5)

之所以发生这种情况,是因为您正在使用MessageBox.Show进行调试打印,并且显示模式消息框会阻止代码流,但不会阻止UI线程(否则您无法与消息框进行交互)。

因此,当通过显示消息框来“阻止”第一个延续时,可以运行下一个延续,然后是第三个延续。它们都被显示消息框“阻止”,但UI线程本身不是。

这就是为什么他们都显示0,并且只有当您释放消息框时,他们才能继续运行并增加变量。

如果您在显示消息框之前使用Console.WriteLine打印值,则可以看到:

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;
}

您会注意到0在1秒后完全打印了3次,而不是在您关闭消息框之后。

基本上,延续并发运行,但不是并行使用相同的线程,因为其中包含MessageBox.Show

如果您使用Console.WriteLine代替MessageBox.Show,您会看到该值一次增加一个:

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

输出:

0
1
2