1. ホーム
  2. c#

[解決済み] C#で同期メソッドから非同期メソッドを呼び出すには?

2022-03-17 15:25:40

質問

私は public async void Foo() メソッドを呼び出したい。これまでのところ、私がMSDNドキュメントから見たのは、非同期メソッドを介して非同期メソッドを呼び出すことだけですが、私のプログラム全体は非同期メソッドで構築されていません。

このようなことは可能なのでしょうか?

これらのメソッドを非同期メソッドから呼び出す例を一つ紹介します。
チュートリアル 非同期とAwaitを使ったウェブアクセス(C#とVisual Basic)

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

どのように解決するのですか?

非同期プログラミングは、コードベースを通して成長します。それは ゾンビウィルスに例えられる . 一番の解決策は、成長を許すことですが、そうもいかない場合もあります。

の中にいくつかの型を書きました。 Nito.AsyncEx ライブラリは、部分的に非同期なコードベースに対処するためのものです。しかし、すべての状況でうまくいく解決策はありません。

解決策A

コンテキストに同期する必要のない単純な非同期メソッドがある場合、そのメソッドに Task.WaitAndUnwrapException :

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

あなたがすること ない を使用したい。 Task.Wait または Task.Result で例外を包むからです。 AggregateException .

この解決策は、以下の場合にのみ適切です。 MyAsyncMethod は、そのコンテキストに同期して戻ってこない。言い換えれば、すべての awaitMyAsyncMethod で終わらせる必要があります。 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で発生する、かなり微妙なデッドロックの可能性があるためです。

  1. 同期メソッドが非同期メソッドを呼び出して Task .
  2. 同期型メソッドは Task .
  3. async メソッドでは await なし ConfigureAwait .
  4. Task のときにしか完了しないので、この状況では完了することができません。 async メソッドが終了します。 async メソッドが完了できないのは、その継続を SynchronizationContext そして、WinForms/WPF/SL/ASP.NET はそのコンテキストで同期メソッドが既に実行されているため、継続の実行を許可しない。

このようなこともあり、このような場合は ConfigureAwait(false) をすべての 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-05-01に更新しました。 現在の"restorward practices"は、以下の通りです。 MSDN記事はこちら .