アプリケーションの起動時に設定ファイルを読み込んで読み取ろうとしていますが、約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)."
が表示されます。
ここで何が起こっているのか誰かが知っていますか?
デフォルトでは、まだ完了していないawait
Task
を実行すると、キャプチャされたコンテキスト(この場合はUIコンテキスト)でメソッドが再開されます。
したがって、コードが失敗する理由は次のとおりです。
OnLaunched
はLoad
を呼び出します(UIコンテキスト内)。Load
が待っています。これにより、Load
メソッドは不完全なタスクを返し、その完了を後でスケジュールします。この継続は、UIコンテキストに対してスケジュールされています。OnLaunched
から返されたタスクのLoad
ブロック。これにより、UIスレッドがブロックされます。GetFileAsync
は最終的に完了し、Load
の継続を実行しようとします。Load
の継続は、UIスレッドが使用可能になるのを待って、UIコンテキストで実行できるようにします。OnLaunched
はLoad
が完了するのを待っており(そうすることでUIスレッドをブロックします)、Load
はUIスレッドが解放されるのを待っています。デッドロック。これらのベストプラクティスは、この状況を回避します。
async
メソッドでは、可能な限りConfigureAwait(false)
を使用します。あなたの場合、これはawait folder.GetFileAsync("filename.xml");
をawait folder.GetFileAsync("filename.xml").ConfigureAwait(false);
に変更します。Task
sでブロックしないでください。ずっと下にあるのはasync
です。つまり、Wait
をawait
に置き換えます。詳細については:
async
/await
intro post これには、Task
待機者がSynchronizationContext
を使用する方法の簡単な説明が含まれ、いくつかのベストプラクティスが紹介されています。更新、2012-07-13:この回答を組み込みました ブログ投稿に 。