あなたがサーバサイドコード(つまりいくつかのApiController
)を持っていて、そしてあなたの関数が非同期である - それでそれらがTask<SomeObject>
を返す - それはあなたがいつあなたがConfigureAwait(false)
と呼ぶ関数を待つことがベストプラクティスと考えられますか?
スレッドコンテキストを元のスレッドコンテキストに戻す必要がないため、パフォーマンスが向上していることを私は読みました。ただし、ASP.NET Web Apiでは、リクエストが1つのスレッドで受信され、ApiController
関数の最終結果が返されるときに、何らかの関数を待ってConfigureAwait(false)
を呼び出すと、別のスレッドに移動する可能性があります。
私が話している内容の例を以下に入力しました。
public class CustomerController : ApiController
{
public async Task<Customer> Get(int id)
{
// you are on a particular thread here
var customer = await SomeAsyncFunctionThatGetsCustomer(id).ConfigureAwait(false);
// now you are on a different thread! will that cause problems?
return customer;
}
}
更新: ASP.NET CoreにはSynchronizationContext
がありません。ASP.NETCoreを使用している場合は、ConfigureAwait(false)
を使用するかどうかは関係ありません。
ASP.NETの「フル」または「クラシック」などには、この回答の残りの部分が適用されます。
元の投稿(コア以外のASP.NET用):
ASP.NETチームによるこのビデオでは、ASP.NETでasync
を使用するための最良の情報を得ています。
スレッドコンテキストを元のスレッドコンテキストに戻す必要がないため、パフォーマンスが向上していることを私は読みました。
これは、「同期」する必要があるUIスレッドが1つしかないUIアプリケーションにも当てはまります。
ASP.NETでは、状況はもう少し複雑です。 async
メソッドが実行を再開すると、ASP.NETスレッドプールからスレッドを取得します。 ConfigureAwait(false)
を使用してコンテキストキャプチャを無効にした場合、スレッドはそのままメソッドの実行を続けます。コンテキストキャプチャを無効にしないと、スレッドはリクエストコンテキストを再入力してからメソッドの実行を続けます。
したがって、ConfigureAwait(false)
はASP.NETでのスレッドジャンプを節約しません。リクエストコンテキストを再入力する手間が省けますが、通常これは非常に高速です。 ConfigureAwait(false)
couldは、リクエストを少量の並列処理で処理しようとしている場合に役立ちますが、実際にはTPLがこれらのシナリオのほとんどに適しています。
ただし、ASP.NET Web Apiでは、リクエストが1つのスレッドで受信され、何らかの関数を待ってConfigureAwait(false)を呼び出すと、ApiController関数の最終結果が返されるときに別のスレッドに移動する可能性があります。 。
実際には、await
を実行するだけでそれを実行できます。 async
メソッドがawait
にヒットすると、methodはブロックされますが、threadはスレッドプールに戻ります。メソッドを続行する準備が整うと、スレッドプールからスレッドが捕捉され、メソッドの再開に使用されます。
ASP.NETでConfigureAwait
がもたらす唯一の違いは、そのスレッドがメソッドの再開時に要求コンテキストに入るかどうかです。
私は私の MSDNのSynchronizationContext
に関する記事と私の async
のイントロブログ記事 )にもっと詳しい情報があります。
あなたの質問に対する簡単な答え:いいえ。そのようなアプリケーションレベルでConfigureAwait(false)
を呼び出すべきではありません。
TL、DR版の長い答え:あなたがあなたの消費者を知らず、同期コンテキストを必要としないライブラリを書いているなら(これは私が信じているライブラリではいけない)、あなたは常にConfigureAwait(false)
を使うべきです。さもなければ、あなたのライブラリの消費者はあなたの非同期メソッドをブロックする方法で消費することによってデッドロックに直面するかもしれません。これは状況によって異なります。
これがConfigureAwait
メソッドの重要性についてのもう少し詳細な説明です(私のブログ投稿からの引用):
あなたがawaitキーワードでメソッドを待っているとき、コンパイラはあなたの代わりにたくさんのコードを生成します。このアクションの目的の1つは、UI(またはメイン)スレッドとの同期を処理することです。この機能の重要な要素は、現在のスレッドの同期コンテキストを取得する
SynchronizationContext.Current
です。SynchronizationContext.Current
は現在の環境に応じて設定されます。TaskのGetAwaiter
メソッドはSynchronizationContext.Current
を検索します。現在の同期コンテキストがnullでない場合、その待機者に渡される継続はその同期コンテキストにポストバックされます。新しい非同期言語機能を使用するメソッドをブロッキング方式で使用する場合、利用可能なSynchronizationContextがあると、デッドロックが発生します。このようなメソッドをブロッキング的に使用している場合(Waitメソッドでタスクを待機するか、タスクのResultプロパティから直接結果を取得する)、同時にメインスレッドをブロックします。最終的にタスクがスレッドプール内のそのメソッド内で完了すると、
SynchronizationContext.Current
が使用可能でキャプチャーされているので、メインスレッドにポストバックするために継続を呼び出します。しかし、ここには問題があります。UIスレッドがブロックされていて、デッドロックが発生しています。
また、あなたの質問にぴったりの2つの素晴らしい記事があります。
最後に、 Lucian Wischik からの非常に短いビデオがあります: 非同期ライブラリメソッドはTask.ConfigureAwait(false)の使用を考慮する必要があります 。
お役に立てれば。
私がConfigureAwait(false)を使って見つけた最大の欠点は、スレッドカルチャがシステムのデフォルトに戻されることです。カルチャを設定した場合.
<system.web>
<globalization culture="en-AU" uiCulture="en-AU" />
...
カルチャがen-USに設定されているサーバーでホストしている場合は、ConfigureAwait(false)がCultureInfo.CurrentCultureからen-AUが返される前、およびen-USになった後がわかります。すなわち.
// CultureInfo.CurrentCulture ~ {en-AU}
await xxxx.ConfigureAwait(false);
// CultureInfo.CurrentCulture ~ {en-US}
アプリケーションがカルチャ固有のデータフォーマットを必要とするものを実行している場合は、ConfigureAwait(false)を使用するときにこのことに注意する必要があります。
私はTask
の実装についていくつかの一般的な考えを持っています:
using
を使用します。ConfigureAwait
は4.5で導入されました。 Task
は4.0で導入されました。Task.ContinueWith
のデフォルト実装ではb/cを実行しません。私はこの件に関して 投稿 をいくつか持っていますが、私の考えでは - TugberkのNice answerに加えて - すべてのAPIを非同期にして理想的にはコンテキストを流すべきです。 非同期をしているので、待つ代わりに単に継続を使うことができます。ライブラリーでは待機が行われず、フローが維持されるのでデッドロックは発生しません(HttpContextなど)。
ライブラリが同期APIを公開しているが、別の非同期APIを使用している場合に問題があります。したがって、コードでWait()
/Result
を使用する必要があります。