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

jok*_*efe 26 c# file-io async-await windows-8 windows-runtime

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

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

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

App.xaml.cs:

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
    FileLoader.Load().Wait();

    // File-load dependent stuff
}
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

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

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

Ste*_*ary 51

默认情况下,当你await一个Task还没有完成,该方法在捕捉的上下文恢复(在这种情况下,UI方面).

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

  • OnLaunched调用Load(在UI上下文中).
  • Load等待.这会导致该Load方法返回一个不完整的任务,并安排其完成以供日后使用.此延续计划用于UI上下文.
  • OnLaunched从中返回的任务块Load.这会阻止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.

欲获得更多信息:

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

  • 我希望MS死了。由于提供了如此疯狂的东西,他们绝对是在寻找人。他们为什么不留下老式线程和同步原语?!白痴。 (2认同)