28

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

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

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

应用程序.xaml.cs:

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

    // File-load dependent stuff
}

文件加载器.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)."

有谁知道这里发生了什么?

4

1 回答 1

54

默认情况下,当您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. 不要在Tasks 上阻塞;它async一直在下降。换句话说,替换Waitawait

了解更多信息:

2012 年 7 月 13 日更新:将此答案合并到博客文章中。

于 2012-07-03T20:03:28.310 回答