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个(如我的示例中所示)。你能解释一下原因吗?这要归咎于底层的Win32MessageBox
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