C# 值在异步方法恢复后未更新

C# 值在异步方法恢复后未更新,c#,.net,winforms,async-await,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.ToSt

查看此代码:

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;
}
一定发生过

换句话说,

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

GUI线程最终必须运行:

续#1

续#2

续#3


看起来续集不是作为一个整体安排的,而是作为共享量子安排的

发生这种情况是因为您正在使用
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次的

基本上,continuations是并行运行的,但不是使用同一个线程并行运行的,因为其中有
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

@我不明白,每个延续都有自己的gui线程。这里没有涉及其他线程。为什么要锁?这不是(!)在线程池线程中运行,这意味着没有要保留的上下文(每个延续可以在另一个线程池线程中运行)…更改
BBB
以接受并使用延迟值:
async Task BBB(SharedData,int delay)
,然后传递不同的值,而不是相同的
1
。想知道为什么现在一次看到三个消息框而不是一个@是的,我想知道:-)你能解释一下吗?同时显示3个MSgBox,而不是一次显示1个(如我的示例中所示)。你能解释一下原因吗?这要归咎于底层的Win32
MessageBox
API。如果使用
Form.ShowDialog
,您将看到所有3个框的顺序都是正确的1-2-3。使用
MessageBox
,其他两个框仍然存在,只是不可见。他们确实已经开始了嵌套的模式消息循环,一个接一个,因为
等待任务。延迟(1000)
继续仍然会被压缩。一个嵌套的消息循环开始了,这是由哪个继续赢得比赛引起的,因此显示窗口的代码直到消息循环终止才开始。Royi,@StephenCleary's比我上面的评论解释得更详细。第二段(对我来说)不清楚:当显示消息框“阻止”第一个继续时,可以运行下一个继续,然后运行第三个继续?你能展示一下这些步骤吗?@RoyiNamir显示了一个模式消息框,阻止了当前的代码流,但它不会阻止线程(否则应用程序将冻结)。这意味着第一个continuation被卡住了,但是下一个可以在UI线程上运行值尚未增加?@RoyiNamir完全正确。这就是消息循环的工作方式。MSDN论坛上也出现了类似的问题。@RoyiNamir它会阻止流,但不会阻止线程本身。它不能阻止线程本身,因为它是绘制消息框的线程。如果线程被阻止,应用程序将冻结并没有响应。
 MessageBox.Show(data.Value.ToString()); // Why data.Value still "0" ??
 data.Value = data.Value + 1;
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;
}
async Task BBB(SharedData data)
{
    await Task.Delay(TimeSpan.FromSeconds(1));
    Console.WriteLine(data.Value);
    data.Value = data.Value + 1;
}
0
1
2