非同期プログラミングは、コードベースを通じて「成長」します。ゾンビウイルスと比較されています。最善の解決策は、それを成長させることですが、それができない場合もあります。
部分的に非同期のコードベースを処理するために、Nito.AsyncExライブラリにいくつかの型を記述しました。ただし、すべての状況で機能する解決策はありません。
ソリューションA
コンテキストに同期する必要のない単純な非同期メソッドがある場合は、次を使用できますTask.WaitAndUnwrapException
。
var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();
あなたはないではない使用したいTask.Wait
か、Task.Result
彼らは例外をラップしているためAggregateException
。
このソリューションは、MyAsyncMethod
がそのコンテキストに同期しない場合にのみ適切です。つまり、すべての入力await
はMyAsyncMethod
で終わる必要がありConfigureAwait(false)
ます。つまり、UI要素を更新したり、ASP.NET要求コンテキストにアクセスしたりすることはできません。
ソリューションB
MyAsyncMethod
コンテキストに同期する必要がある場合はAsyncContext.RunTask
、ネストされたコンテキストを提供するために使用できる場合があります。
var result = AsyncContext.RunTask(MyAsyncMethod).Result;
* 2014年4月14日更新:ライブラリの最新バージョンでは、APIは次のとおりです。
var result = AsyncContext.Run(MyAsyncMethod);
(例外が伝播するTask.Result
ため、この例でRunTask
は使用しても問題ありませTask
ん)。
AsyncContext.RunTask
代わりに必要になるTask.WaitAndUnwrapException
理由は、WinForms / WPF / SL / ASP.NETで発生するかなり微妙なデッドロックの可能性があるためです。
- 同期メソッドは非同期メソッドを呼び出し、を取得し
Task
ます。
- 同期メソッドは、でブロッキング待機を行い
Task
ます。
async
この方法は、使用していますawait
なしConfigureAwait
。
Task
ときにのみ完了するので、このような状況で完了することができないasync
方法で、完成です。async
この方法は、完全ではないにその継続をスケジュールしようとしている可能性があるためSynchronizationContext
、とのWinForms / WPF / SL / ASP.NETは、同期方法は、すでにそのコンテキストで実行されているので、継続が実行することはできません。
これが、ConfigureAwait(false)
すべてのasync
メソッド内で可能な限り使用することが推奨される理由の1つです。
ソリューションC
AsyncContext.RunTask
すべてのシナリオで機能するわけではありません。たとえば、async
メソッドがUIイベントの完了を必要とする何かを待機している場合、ネストされたコンテキストでもデッドロックが発生します。その場合、async
スレッドプールでメソッドを開始できます。
var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();
ただし、このソリューションではMyAsyncMethod
、スレッドプールコンテキストで機能するが必要です。したがって、UI要素を更新したり、ASP.NET要求コンテキストにアクセスしたりすることはできません。その場合は、ConfigureAwait(false)
そのawait
ステートメントに追加して、ソリューションAを使用することもできます。
更新、2019-05-01:現在の「最悪の慣行」は、こちらのMSDN記事にあります。