web-dev-qa-db-ja.com

Task.Runは、ASP .NET MVC Webアプリケーションで悪い習慣と見なされていますか?

バックグラウンド

現在、ASP .NET MVC 5、Angular.JS 1.4、Web API 2、およびEntity Framework 6に依存するWebアプリケーションを開発しています。スケーラビリティの理由から、Webアプリケーションの負荷はasync/awaitパターン:このドメインでは、CPUを集中的に使用する計算が必要であり、数秒(10秒未満)かかります。過去には、計算を高速化するためにTask.Runを使用していたチームメンバーがいました。 ASP .NET MVCまたはWeb APIコントローラーは悪いプラクティスと見なされます(スレッドはIISによって認識されないため、AppDomainリサイクルでは考慮されません=>参照 Stephen Clearyのブログ投稿 =)、彼らはConfigureAwait(false)を使用しました。

public async Task CalculateAsync(double param1, double param2)
{
    // CalculateSync is synchronous and cpu-intensive (<10s)
    await Task.Run(() => this.CalculateSync(param1, param2))).ConfigureAwait(false);
}

ご質問

  • CPUにバインドされた操作に非同期Web APIコントローラーでTask.Runを使用することでパフォーマンス上の利点はありますか?
  • ConfigureAwait(false)は、余分なスレッドの作成を本当に回避しますか?
44
Fabe

CPUバインド操作のために非同期Web APIコントローラーでTask.Runを使用することでパフォーマンス上の利点はありますか?

ゼロ。なし。実際、新しいスレッドを生成することでパフォーマンスを妨げています。 Webアプリケーションのコンテキスト内では、スレッドの生成は「バックグラウンド」での実行とは異なります。これは、Webリクエストの性質によるものです。着信要求があると、要求を処理するためにスレッドがプールから取得されます。非同期を使用すると、リクエストの終了前にスレッドを返すことができますifスレッドが待機状態(アイドル)の場合のみ。作業を行うためにスレッドを生成すると、プライマリスレッドが効果的にアイドル状態になり、プールに返されるようになりますが、アクティブなスレッドがまだあります。元のスレッドをプールに返すことは、その時点では何もしません。次に、新しいスレッドが作業を終了したら、プールからメインスレッドを要求し、最後に応答を返さなければなりません。 all作業が完了するまで応答を返すことができないため、1スレッドまたは100スレッド、非同期または同期のいずれを使用しても、すべてが完了するまで応答を返すことはできません。したがって、追加のスレッドを使用しても、オーバーヘッドが追加されるだけです。

ConfigureAwait(false)は、余分なスレッドの作成を実際に回避しますか?

いいえ、またはより適切に、それはそれについてではありません。 ConfigureAwaitは最適化のヒントにすぎず、元のコンテキストがスレッドのジャンプ間で維持されるかどうかのみを決定します。長いことも短いこともありますが、スレッドの作成とは関係がなく、少なくともASP.NETアプリケーションのコンテキストでは、どちらの方法でもパフォーマンスへの影響は無視できます。

51
Chris Pratt

CPUバウンド操作のために非同期Web APIコントローラーでTask.Runを使用することでパフォーマンス上の利点はありますか?

いいえ。CPUにバインドされているかどうかは関係ありません。

Task.Runは、ThreadPoolスレッドへの作業をオフロードします。 Web APIリクエストはすでにThreadPoolスレッドを使用しているため、理由なく別のスレッドにオフロードすることでスケーラビリティを制限しているだけです。

これは、UIスレッドが特別な単一スレッドであるUIアプリで役立ちます。

ConfigureAwait(false)は、余分なスレッドの作成を本当に回避しますか?

何らかの方法で作成するスレッドには影響しません。キャプチャされたSynchronizationContextで再開するかどうかを設定するだけです。

21
i3arnon

CPUにバインドされた操作に非同期Web APIコントローラーでTask.Runを使用することでパフォーマンス上の利点はありますか?

本当に何が起こるか考えてください-Task.Run()はスレッドプールにタスクを作成し、await演算子はスレッドを解放します(スタックのすべてのメソッドも待機していると仮定しています)。これでスレッドがプールに戻り、同じタスクを取得する可能性があります!このシナリオでは、明らかにパフォーマンスの向上はありません。実際、パフォーマンスが低下しています。ただし、スレッドが別のタスクを選択した場合(おそらくこれが発生します)、別のスレッドはCalculateSync()タスクを選択し、前者が停止した場所から続行する必要があります。そもそも元のスレッドにCalculateSync()を実行させ、タスクを関与させず、他のスレッドに他のキューに入れられたタスクを持たせる方が理にかなっているでしょう。

ConfigureAwait(false)は、余分なスレッドの作成を本当に回避しますか?

どういたしまして。それは単に、継続が呼び出し元のコンテキストで実行されるべきではないと指摘するだけです。

9
shay__

考慮すべき点がもう1つあります。 APIがCPU集中型タスクを実行していると言った場合、async/await helpを使用して別のスレッドでプロセスを実行し、別の要求のためにアプリプールを解放します。 async/awaitを正しく使用すると、Web APIは1秒あたりのリクエスト数をより多く処理できることを意味します。

あなたの場合、this.CalculateSync(param1, param2)は非同期メソッドのように見えるので、これを非同期で呼び出すにはTask.Runを使用する必要があります。

実際にパフォーマンスが低下するため、.ConfigureAwait(false)を削除することをお勧めします。

2
Neha Jain