5

我有一个问题如何在 WPF 生命周期方法(使用 Caliburn-Micro 框架)中等待异步方法(例如 OnActivate、OnInitialized、OnExit - 它直接绑定到Application.Exit事件)

这篇文章准确地描述了我的问题: http: //mark.mymonster.nl/2013/07/10/donrsquot-make-your-application-lifetime-events-async-void(现在我正在考虑使用本文中的解决方案,但乍一看似乎有点矫枉过正)

我需要在我的 OnExit 处理程序中等待一些异步方法,所以我将它作为异步。它有效。有点儿。我不明白为什么?,但是在调用 Application.Exit 事件时,它会以某种方式等待方法完成,即使处理程序是异步无效的。你能解释一下这是怎么可能的吗?这安全吗?还是只是巧合?Async void 应该只用于顶级事件,是这种情况吗?

我查看了系统的代码。绑定看起来像这样:

public event EventHandler Exit
{
  add
  {
    XcpImports.CheckThread();
    this.AddEventListener(DependencyProperty.RegisterCoreProperty(20053U, (Type) null), (Delegate) value);
  }
  remove
  {
    XcpImports.CheckThread();
    this.RemoveEventListener(DependencyProperty.RegisterCoreProperty(20053U, (Type) null), (Delegate) value);
  }
}

这真的很神秘,我无法通过调用此事件来了解 .net 框架中真正发生的事情。

同样奇怪的是,当我不使用ConfigureAwait(false)时,在处理程序中调用await Task.Delay(1)会导致死锁。所以我会说.Wait()在 .net 代码的深处使用。

注意:当我使 OnActivate、OnInitialized 处理程序异步时,正如预期的那样,页面不会等待处理程序完成。

谢谢你的回答!

4

1 回答 1

11

理论上,框架可以检测到使用async void并等待async void方法返回。我在我的文章中SynchronizationContext描述了细节。AFAIK,ASP.NET 是唯一将等待处理程序的内置框架async void

WPF对方法没有任何特殊处理async void。因此,您的退出处理程序正在完成的事实只是巧合。我怀疑您的操作await已经完成或非常快,这允许您的处理程序同步完成。

也就是说,我不推荐您引用的文章中的解决方案。相反,处理窗口的Closing事件,启动您需要执行的任何异步保存,然后取消关闭命令(并考虑立即隐藏窗口)。异步操作完成后,再次关闭窗口(并允许这次关闭)。我使用这种模式来执行异步窗口级“关闭”动画。

我无法重现您描述的僵局。我创建了一个新的 .NET 4.5 WPF 应用程序并添加了一个退出处理程序,如下所示:

private async void Application_Exit(object sender, ExitEventArgs e)
{
    await Task.Delay(1);
}

但没有观察到死锁。事实上,即使使用 using Task.Yield,在await执行之后什么也没有,这是我所期望的。

于 2013-09-05T14:02:49.077 回答