调用等待GetFileAsync()永远不会返回,应用程序在WinRT应用程序中挂起

我试图在应用程序启动时加载和读取设置文件,大约90%的时间, await GetFileAsync("filename.xml"); 永远不会返回,因此,悬挂应用程序。

大约四分之一的时间,如果我单步执行代码,它实际上将返回并读取文件。

这是代码的一个非常简化的版本:

App.xaml.cs:

 protected override void OnLaunched(LaunchActivatedEventArgs args) { FileLoader.Load().Wait(); // File-load dependent stuff } 

FileLoader.cs:

 public async static Task Load() { StorageFolder folder = ApplicationData.Current.LocalFolder; StorageFile file; bool fileExists = true; try { // The following line (often) never returns file = await folder.GetFileAsync("filename.xml"); { catch { fileExists = false; } // Do stuff with loaded file } 

如果我在Visual Studio中观察输出窗口,经过一段时间的等待后,我得到"The thread '' (0x30c) has exited with code 0 (0x0)."

有没有人知道这里发生了什么?

默认情况下,当您await尚未完成的Task时,该方法将在捕获的上下文(在本例中为UI上下文)中恢复。

所以,这就是你的代码失败的原因:

  • OnLaunched调用Load (在UI上下文中)。
  • Load等待。 这会导致Load方法返回一个不完整的任务,并安排其完成以供日后使用。 此延续计划用于UI上下文。
  • OnLaunchedLoad返回的任务块。 这会阻止UI线程。
  • GetFileAsync最终完成,并尝试运行Load的延续。
  • Load的延续等待UI线程可用,以便它可以在UI上下文中执行。
  • 此时, OnLaunched正在等待Load完成(通过这样做来阻止UI线程),并且Load正在等待UI线程空闲。 僵局。

这些最佳实践避免了这种情况:

  1. 在“库” async方法中,尽可能使用ConfigureAwait(false) 。 在你的情况下,这将改变await folder.GetFileAsync("filename.xml"); await folder.GetFileAsync("filename.xml").ConfigureAwait(false);
  2. 不要阻止Task ; 它一直是async的。 换句话说,用Wait替换await

欲获得更多信息:

  • 等待,UI和死锁! 天啊!
  • 我的async / await简介post ,其中包括Task awaiters如何使用SynchronizationContext的简要描述,并介绍了一些最佳实践。
  • Async / Await FAQ ,它详细介绍了上下文。
  • 这篇MSDN论坛post 。
  • Stephen Toub解决了这个僵局 , Lucian Wischik也是如此 。

更新,2012-07-13:将此答案纳入博客文章 。