查看此代码:
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;
看起来这些延续不是作为一个整体而是作为共享量子安排的?
答案 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