![](/img/trans.png)
[英]async/await with ConfigureAwait's continueOnCapturedContext parameter and SynchronizationContext for asynchronous continuations
[英]async-await's continuations bursts — behave differently?
我有一个winform代码,点击按钮后运行:
void button1_Click(object sender, EventArgs e)
{
AAA();
}
async Task BBB( int delay)
{
await Task.Delay(TimeSpan.FromSeconds(delay));
MessageBox.Show("hello");
}
async Task AAA()
{
var task1 = BBB(1); // <--- notice delay=1;
var task2 = BBB(1); // <--- notice delay=1;
var task3 = BBB(1); // <--- notice delay=1;
await Task.WhenAll(task1, task2, task3);
}
题 :
为什么我会在delay=1
时一次看到一个MessageBox:
但如果我把延迟改为: 1,2,3
-
var task1 = BBB(1);
var task2 = BBB(2);
var task3 = BBB(3);
我看到 - 3个消息框甚至没有点击任何消息框?
请注意,嵌套的消息循环是邪恶的,因为意外的重入是刚刚太难(tm)。
我认为解释这种行为有两个关键的理解。 首先是异步延续 - 就像所有其他“运行此任意代码”Win32消息一样 - 具有比其他消息更高的优先级 。 第二个是, 在运行嵌套消息循环时 ,有一个长期存在的Win32传统,即发送消息并同步阻止响应。 (另一方面,我个人认为,这种可怕的重入 - Win32 API的所有设计都是Windows上绝大多数应用程序错误的原因)。
如果以保留堆栈跟踪的方式运行代码,则可以更清楚地看到发生了什么:
void button1_Click(object sender, EventArgs e)
{
AAA();
}
private List<string> stacks = new List<string>();
async Task BBB(int delay)
{
await Task.Delay(TimeSpan.FromSeconds(delay));
var stack = new StackTrace().ToString();
stacks.Add(stack);
MessageBox.Show(stack);
}
async Task AAA()
{
var task1 = BBB(1); // <--- notice delay=1;
var task2 = BBB(1); // <--- notice delay=1;
var task3 = BBB(1); // <--- notice delay=1;
await Task.WhenAll(task1, task2, task3);
Clipboard.SetText(string.Join("\r\n\r\n", stacks));
}
在对话框全部关闭(首先是最小,然后是中等,然后是最大)之后, 将对话文本 (最大堆栈,然后是中等,然后最小)与剪贴板进行比较。 很明显,对话框的显示顺序相反。
我相信这样的事情正在发生,但没有信心肯定地说:
MessageBox.Show
。 MessageBox
函数开始嵌套消息循环,并且开始建立与消息本身的实际对话(即,设置标题,文本等)。 请注意,这些调用会输出消息,但它们尚未准备好显示对话框。 MessageBox.Show
,然后在这些设置消息前面跳转并跳转。 当您将计时更改为1, 2, 3
,您仍将在剪贴板中获得相同的堆栈,但您将看到对话文本现在按顺序排列(最小堆栈先是中等,然后是最大堆栈)。 这是因为每个MessageBox.Show
都有足够的时间来设置消息框并建立其消息循环,并在其上面的下一个图层之前显示对话框。
理论上, MessageBox.ShowAsync
API可以避免这种奇怪的行为,完全避免嵌套循环。 不过,我不会屏住呼吸。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.