非同期プログラミングは、コードベースを通じて「成長」します。ゾンビウイルスと比較されています。最善の解決策は、それを成長させることですが、それができない場合もあります。
部分的に非同期のコードベースを処理するために、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記事にあります。