web-dev-qa-db-ja.com

GetFileAsync()を待機するための呼び出しが戻らず、アプリがWinRTアプリでハングする

アプリケーションの起動時に設定ファイルを読み込んで読み取ろうとしていますが、約90%の確率で、await GetFileAsync("filename.xml");が戻らないため、アプリケーションがハングします。

約4分の1の時間、コードをステップ実行すると、実際に戻ってファイルが読み取られます。

コードの非常に簡略化されたバージョンを次に示します。

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)."が表示されます。

ここで何が起こっているのか誰かが知っていますか?

27
jokeefe

デフォルトでは、まだ完了していないawaitTaskを実行すると、キャプチャされたコンテキスト(この場合はUIコンテキスト)でメソッドが再開されます。

したがって、コードが失敗する理由は次のとおりです。

  • OnLaunchedLoadを呼び出します(UIコンテキスト内)。
  • Loadが待っています。これにより、Loadメソッドは不完全なタスクを返し、その完了を後でスケジュールします。この継続は、UIコンテキストに対してスケジュールされています。
  • OnLaunchedから返されたタスクのLoadブロック。これにより、UIスレッドがブロックされます。
  • GetFileAsyncは最終的に完了し、Loadの継続を実行しようとします。
  • Loadの継続は、UIスレッドが使用可能になるのを待って、UIコンテキストで実行できるようにします。
  • この時点で、OnLaunchedLoadが完了するのを待っており(そうすることでUIスレッドをブロックします)、LoadはUIスレッドが解放されるのを待っています。デッドロック。

これらのベストプラクティスは、この状況を回避します。

  1. 「ライブラリ」のasyncメソッドでは、可能な限りConfigureAwait(false)を使用します。あなたの場合、これはawait folder.GetFileAsync("filename.xml");await folder.GetFileAsync("filename.xml").ConfigureAwait(false);に変更します。
  2. Tasksでブロックしないでください。ずっと下にあるのはasyncです。つまり、Waitawaitに置き換えます。

詳細については:

更新、2012-07-13:この回答を組み込みました ブログ投稿に

51
Stephen Cleary