アプリケーションの起動時に設定ファイルを読み込んで読み込もうとしていますが、約 90% の確率で設定ファイルawait GetFileAsync("filename.xml");
が返されず、アプリケーションがハングします。
約 4 分の 1 の確率で、コードをステップ実行すると、実際に戻ってファイルを読み取ります。
以下に、非常に簡略化されたバージョンのコードを示します。
App.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)."
ここで何が起こっているのか誰か分かりますか?
ベストアンサー1
デフォルトでは、まだ完了していない操作を実行すると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);
。 - sでブロックしないでください。s はずっと下に
Task
あります。つまり、に置き換えます。async
Wait
await
詳細については:
- Await、UI、そしてデッドロック! ああ、大変!
- 私の
async
/await
紹介記事Task
では、 awaiters の使用方法の簡単な説明SynchronizationContext
と、いくつかのベスト プラクティスが紹介されています。 - のAsync/Await に関するよくある質問では、コンテキストについてさらに詳しく説明します。
- これMSDNフォーラム投稿。
- スティーブン・トーブこの行き詰まりをデモする、 そしてルシアン・ウィシックも同様だ。
更新、2012-07-13:この回答を組み込んだブログ記事に。