C# で同期メソッドから非同期メソッドを呼び出すにはどうすればよいでしょうか? 質問する

C# で同期メソッドから非同期メソッドを呼び出すにはどうすればよいでしょうか? 質問する

public async Task Foo()同期メソッドから呼び出したいメソッドがあります。 これまで MSDN ドキュメントで確認したのは、メソッドasync経由でメソッドを呼び出すことasyncだけですが、プログラム全体がasyncメソッドで構築されているわけではありません。

そんなことが可能なのでしょうか?

非同期メソッドからこれらのメソッドを呼び出す例を次に示します。
チュートリアル: Async と Await を使用して Web にアクセスする (C# および Visual Basic)

async現在、これらのメソッドを同期メソッドから呼び出すことを検討しています。

ベストアンサー1

非同期プログラミングはコードベースを通じて「成長」します。ゾンビウイルスと比較すると最善の解決策は成長させることですが、それが不可能な場合もあります。

私はいくつかのタイプを書いてきましたNito.AsyncEx部分的に非同期のコード ベースを処理するためのライブラリ。ただし、あらゆる状況で機能するソリューションはありません。

解決策A

コンテキストに同期する必要のない単純な非同期メソッドがある場合は、次を使用できますTask.WaitAndUnwrapException

var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();

またはは例外を でラップするため、使用しないでくださいTask.WaitTask.ResultAggregateException

このソリューションは、 がそのコンテキストに同期しない場合にのみ適切です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ため、この例では使用しても問題ありません)。RunTaskTask

AsyncContext.RunTaskの代わりに が必要になる理由は、Task.WaitAndUnwrapExceptionWinForms/WPF/SL/ASP.NET で発生する、かなり微妙なデッドロックの可能性があるためです。

  1. 同期メソッドは非同期メソッドを呼び出し、を取得しますTask
  2. 同期メソッドは、 に対してブロッキング待機を実行しますTask
  3. メソッドはwithout をasync使用しますawaitConfigureAwait
  4. この状況では は完了できません。これは、 メソッドが終了したTaskときにのみ完了するためです。メソッドは への継続を にスケジュールしようとしているため完了できません。また、同期メソッドがそのコンテキストで既に実行されているため、WinForms/WPF/SL/ASP.NET では継続の実行が許可されません。asyncasyncSynchronizationContext

これは、可能な限りConfigureAwait(false)すべてのメソッド内で使用することを推奨する理由の 1 つです。async

解決策C

AsyncContext.RunTaskすべてのシナリオで機能するわけではありません。たとえば、メソッドが UI イベントの完了を必要とする何かを待機している場合、ネストされたコンテキストでもデッドロックが発生します。その場合は、スレッド プールでメソッドasyncを開始できます。async

var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();

ただし、このソリューションには、スレッド プール コンテキストで動作する が必要です。そのため、UI 要素を更新したり、ASP.NET 要求コンテキストにアクセスしたりすることはできません。その場合は、ステートメントに追加して、ソリューション A を使用することMyAsyncMethodもできます。ConfigureAwait(false)await

更新: 2015 MSDN記事 '非同期プログラミング - ブラウンフィールド非同期開発' スティーブン・クリアリー著。

おすすめ記事