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

时间:2012-07-03 17:52:55

标签: c# file-io windows-8 windows-runtime async-await

我正在尝试在应用程序启动时加载和读取设置文件,大约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 '<No Name>' (0x30c) has exited with code 0 (0x0)."

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

1 个答案:

答案 0 :(得分: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 s;它一直是async。换句话说,将Wait替换为await
  3. 了解更多信息:

    更新,2012-07-13:纳入此答案into a blog post