非同期プログラミングは、コード ベースを通じて「成長」します。ゾンビウイルスと比較されています。最善の解決策は、成長できるようにすることですが、それが不可能な場合もあります。
部分的に非同期のコード ベースを処理するために、 Nito.AsyncExライブラリにいくつかの型を記述しました。ただし、すべての状況で機能するソリューションはありません。
ソリューション A
コンテキストに同期する必要のない単純な非同期メソッドがある場合は、次を使用できますTask.WaitAndUnwrapException
。
var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();
またはで例外をラップするため、orを使用したくありません。Task.Wait
Task.Result
AggregateException
MyAsyncMethod
このソリューションは、 がそのコンテキストに同期されない場合にのみ適切です。つまり、すべてawait
の inMyAsyncMethod
は で終わる必要があり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
、メソッドを完了できません。同期メソッドがそのコンテキストで既に実行されているため、WinForms/WPF/SL/ASP.NET は継続の実行を許可しません。async
SynchronizationContext
これが、可能な限りConfigureAwait(false)
すべてのメソッド内で使用することをお勧めする理由の 1 つです。async
ソリューション 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 年 5 月 1 日:現在の「最小ワースト プラクティス」は、こちらの MSDN 記事にあります。