值在异步方法恢复后未更新
本文关键字:更新 恢复 异步方法 | 更新日期: 2023-09-27 18:12:40
看看这段代码:
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线程。前面的语句 一定发生了 也就是说 我知道所有的 GUI线程最终必须运行: <子>延续# 1 子> 延续#2 <子>延续# 3 子> 看起来延续不是作为一个整体调度的,而是作为共享量子调度的?data.Value = data.Value + 1;
BBB
都是用相同的data
初始值调用的,但是延续不会并发地发生 MessageBox.Show(data.Value.ToString());
data.Value = data.Value + 1; //So this basically should do 0-->1
....
MessageBox.Show(data.Value.ToString()); // Why data.Value still "0" ??
data.Value = data.Value + 1;
....
MessageBox.Show(data.Value.ToString()); // Why data.Value still "0" ??
data.Value = data.Value + 1;
这是因为你使用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