调用等待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上下文。 -
OnLaunched
从Load
返回的任务块。 这会阻止UI线程。 -
GetFileAsync
最终完成,并尝试运行Load
的延续。 -
Load
的延续等待UI线程可用,以便它可以在UI上下文中执行。 - 此时,
OnLaunched
正在等待Load
完成(通过这样做来阻止UI线程),并且Load
正在等待UI线程空闲。 僵局。
这些最佳实践避免了这种情况:
- 在“库”
async
方法中,尽可能使用ConfigureAwait(false)
。 在你的情况下,这将改变await folder.GetFileAsync("filename.xml");
await folder.GetFileAsync("filename.xml").ConfigureAwait(false);
。 - 不要阻止
Task
; 它一直是async
的。 换句话说,用Wait
替换await
。
欲获得更多信息:
- 等待,UI和死锁! 天啊!
- 我的
async
/await
简介post ,其中包括Task
awaiters如何使用SynchronizationContext
的简要描述,并介绍了一些最佳实践。 - Async / Await FAQ ,它详细介绍了上下文。
- 这篇MSDN论坛post 。
- Stephen Toub解决了这个僵局 , Lucian Wischik也是如此 。
更新,2012-07-13:将此答案纳入博客文章 。