Звонок в ожидании GetFileAsync() никогда не возвращается и приложение зависает в приложении WinRT - программирование

Звонок в ожидании 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 '<No Name>' (0x30c) has exited with code 0 (0x0)."

Кто-нибудь знает, что происходит здесь?

4b9b3361

Ответ 1

По умолчанию, когда вы await a Task, который еще не завершен, метод возобновляется в захваченном контексте (в данном случае контексте пользовательского интерфейса).

Итак, вот почему ваш код не работает:

  • OnLaunched вызывает Load (в контексте пользовательского интерфейса).
  • Load ждет. Это заставляет метод Load возвращать неполную задачу и планировать ее завершение позже. Это продолжение запланировано для контекста пользовательского интерфейса.
  • OnLaunched блокирует задачу, возвращенную из Load. Это блокирует поток пользовательского интерфейса.
  • GetFileAsync в конечном итоге завершает работу и пытается запустить продолжение для Load.
  • Продолжение для Load ожидает, что поток пользовательского интерфейса будет доступен, чтобы он мог выполняться в контексте пользовательского интерфейса.
  • В этот момент OnLaunched ожидает завершения Load (блокируя поток пользовательского интерфейса, делая это), а Load ожидает, что поток пользовательского интерфейса будет бесплатным. Тупик.

Эти лучшие практики избегают этой ситуации:

  • В методах async используйте ConfigureAwait(false), когда это возможно. В вашем случае это изменит await folder.GetFileAsync("filename.xml"); на await folder.GetFileAsync("filename.xml").ConfigureAwait(false);.
  • Не блокировать Task s; он async полностью вниз. Другими словами, замените Wait на await.

Для получения дополнительной информации:

Обновление, 2012-07-13: Включить этот ответ в сообщение в блоге.