非同期操作を同期的に待機し、なぜここでWait()がプログラムをフリーズさせるのか質問する

非同期操作を同期的に待機し、なぜここでWait()がプログラムをフリーズさせるのか質問する

序文: 私は解決策だけでなく説明も求めています。私はすでに解決策を知っています。

タスクベースの非同期パターン (TAP)、async、await に関する MSDN の記事を数日間勉強したにもかかわらず、細かい点のいくつかについてはまだ少し混乱しています。

私は Windows ストア アプリ用のロガーを作成しており、非同期ログと同期ログの両方をサポートしたいと考えています。非同期メソッドは TAP に従い、同期メソッドはこれをすべて非表示にして、通常のメソッドのように見え、機能する必要があります。

これは非同期ログのコアメソッドです。

private async Task WriteToLogAsync(string text)
{
    StorageFolder folder = ApplicationData.Current.LocalFolder;
    StorageFile file = await folder.CreateFileAsync("log.log",
        CreationCollisionOption.OpenIfExists);
    await FileIO.AppendTextAsync(file, text,
        Windows.Storage.Streams.UnicodeEncoding.Utf8);
}

さて、対応する同期メソッドは...

バージョン 1 :

private void WriteToLog(string text)
{
    Task task = WriteToLogAsync(text);
    task.Wait();
}

これは正しいように見えますが、動作しません。プログラム全体が永久にフリーズします。

バージョン2 :

うーん...タスクが開始されなかったのかもしれません。

private void WriteToLog(string text)
{
    Task task = WriteToLogAsync(text);
    task.Start();
    task.Wait();
}

これは投げるInvalidOperationException: Start may not be called on a promise-style task.

バージョン3:

うーん...Task.RunSynchronously期待できそうだ。

private void WriteToLog(string text)
{
    Task task = WriteToLogAsync(text);
    task.RunSynchronously();
}

これは投げるInvalidOperationException: RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method.

バージョン 4 (解決策):

private void WriteToLog(string text)
{
    var task = Task.Run(async () => { await WriteToLogAsync(text); });
    task.Wait();
}

これはうまくいきます。つまり、2 と 3 は間違ったツールです。しかし、1 はどうでしょうか。1 の何が問題で、4 との違いは何でしょうか。1 がフリーズを引き起こす原因は何でしょうか。タスク オブジェクトに何らかの問題があるのでしょうか。明白でないデッドロックがあるのでしょうか。

ベストアンサー1

非同期メソッドの内部awaitでは、UI スレッドに戻ろうとしています。

UI スレッドはタスク全体が完了するのを待機しているため、デッドロックが発生します。

非同期呼び出しを に移動すると、Task.Run()問題は解決します。
非同期呼び出しはスレッド プールのスレッドで実行されるため、UI スレッドに戻ろうとせず、すべてが機能します。

あるいは、StartAsTask().ConfigureAwait(false)内部操作を待機する前に呼び出して、UI スレッドではなくスレッド プールに戻すことで、デッドロックを完全に回避することもできます。

おすすめ記事