【发布时间】:2018-09-03 21:57:18
【问题描述】:
我在使用PerformClick 调用async 事件处理程序的Windows 窗体应用程序中遇到问题。似乎事件处理程序不是await,而是立即返回。我创建了这个简单的应用程序来显示问题(它只是一个带有 3 个按钮的表单,很容易自己测试):
string message = "Not Started";
private async void button1_Click(object sender, EventArgs e)
{
await MyMethodAsync();
}
private void button2_Click(object sender, EventArgs e)
{
button1.PerformClick();
MessageBox.Show(message);
}
private void button3_Click(object sender, EventArgs e)
{
MessageBox.Show(message);
}
private async Task MyMethodAsync()
{
message = "Started";
await Task.Delay(2000);
message = "Finished";
}
这里有趣的问题是,当我点击Button2 时,你认为message 会显示什么?
令人惊讶的是,正如您所料,它显示的是“已开始”,而不是“已完成”。换句话说,它不是await MyMethod(),它只是启动任务,然后继续。
编辑:
在这个简单的代码中,我可以通过直接从 Button2 事件处理程序调用 await Method() 使其工作,如下所示:
private async void button2_Click(object sender, EventArgs e)
{
await MyMethodAsync();
MessageBox.Show(message);
}
现在,它会等待 2 秒并显示“完成”。
这是怎么回事?为什么使用PerformClick时不起作用?
结论:
好的,现在我明白了,结论是:
如果事件处理程序是
async,则永远不要调用PerformClick。不会await!切勿直接调用
async事件处理程序。不会await!
剩下的就是缺少这方面的文档:
-
Button.PerformClick应该在文档页面上有一个警告:
Button.PerformClick "调用 PerformClick 不会等待异步事件处理程序。"
- 调用
async void方法(或事件处理程序)应该给编译器警告:“您正在调用异步 void 方法,它不会被等待!”
【问题讨论】:
-
召唤驱魔人!
-
这就是重点,它不是在等待。
-
不,它在 awaiting,它只是没有阻塞线程/UI,因此您可以继续单击按钮并使其等待多次你想要的。
-
它按预期工作。当调用
await MyMethodAsync();时,系统会检查MyMethodAsync是否已经返回。它不是由于Task.Delay(2000);而导致的,因此它恢复到button2_Click并打印 "Started"。当然message = "Started";已经被执行了。 -
这里没有发生异常情况。正确的思维模式是等待表达式之后之后的代码会稍后执行,就好像它是由计时器激活的一样。除了 ShowDialog() 和可怕的 Application.DoEvents() 之外,没有任何机制可以阻止 UI 线程上的方法。由于引起讨厌的重入错误的可能性很高,async/await 并没有解决这个根本问题。
标签: c# winforms async-await eventhandler