オランダのTechdaysで、Steve Sandersonは C#5、ASP.NET MVC 4、および非同期Webについてのプレゼンテーションを行いました。
彼は、リクエストの完了に時間がかかると、スレッドプールのすべてのスレッドがビジーになり、新しいリクエストが待たなければならないと説明しました。サーバーは負荷を処理できず、すべてが遅くなります。
次に、非同期webrequestsを使用するとパフォーマンスが向上することを示しました。これは、作業が別のスレッドに委任され、スレッドプールが新しい着信要求に迅速に応答できるためです。彼はこれをデモして、50の同時リクエストが最初に50 * 1秒かかることを示しましたが、非同期動作は合計で1,2秒しかありませんでした。
しかし、これを見た後、私はまだいくつかの質問があります。
なぜより大きなスレッドプールを使用できないのですか? async/awaitを使用して別のスレッドをより遅く起動してから、スレッドプールを最初から増やすだけではありませんか?実行中のサーバーが突然スレッドを増やしたり、何かを取得したりするようなものではありませんか?
ユーザーからのリクエストは、非同期スレッドが終了するのを待っています。プールのスレッドが他のことをしている場合、「UI」スレッドはどのようにビジー状態に保たれますか?スティーブは、「何かがいつ終了するかを知っているスマートカーネル」について何かを述べました。これはどのように機能しますか?
これは非常に良い質問であり、非同期IOが非常に重要である理由を理解することが重要です。新しい非同期/待機機能がC#5.0に追加された理由は、非同期の記述を簡素化するためですサーバーでの非同期処理のサポートは新しいものではありませんが、ASP.NET 2.0以降に存在します。
Steveが示したように、同期処理では、ASP.NET(およびWCF)の各要求はスレッドプールから1つのスレッドを取得します。彼がデモした問題は、「thread pool starvation」と呼ばれるよく知られた問題です。サーバー上で同期IOを行うと、スレッドプールスレッドはIOの間ブロックされたまま(何もしない)になります。スレッドプール内のスレッド数に制限があるため、負荷がかかると、すべてのスレッドプールスレッドがIOを待機してブロックされ、リクエストがキューに入れられ始め、応答時間が長くなる状況につながる可能性があります。すべてのスレッドがIOを完了すると、0%に近いCPU占有率が表示されます(応答時間が屋根を通過しても)。
あなたが尋ねているもの(なぜより大きなスレッドプールを使用できないのですか?)は非常に良い質問です。実際のところ、これは、ほとんどの人がこれまでスレッドプールの枯渇の問題を解決してきた方法です。スレッドプールにもっと多くのスレッドがあるだけです。 Microsoftの一部のドキュメントでは、スレッドプールの枯渇が発生する可能性がある状況の修正としても示されています。これは許容できるソリューションであり、C#5.0までは、コードを完全に非同期に書き換えるよりもはるかに簡単でした。
ただし、このアプローチにはいくつかの問題があります。
すべての状況で機能する値はありません:必要なスレッドプールスレッドの数は、IOの継続時間に線形に依存し、サーバーにロードします。残念ながら、IOレイテンシはほとんど予測できません。以下に例を示します。ASP.NETアプリケーションでサードパーティのWebサービスにHTTPリクエストを行い、完了までに約2秒かかります。スレッドプールの枯渇が発生したため、スレッドプールのサイズをたとえば200スレッドに増やした後、再び正常に動作するようになります。問題は、来週、Webサービスに技術的な問題が発生し、応答時間が長くなることです。スレッドが5倍長くブロックされるため、突然のスレッドプールの枯渇がすべて回復したため、5倍に1,000スレッドに増やす必要があります。
スケーラビリティとパフォーマンス:2番目の問題は、それを行うと、リクエストごとに1つのスレッドを使用することになります。スレッドは高価なリソースです。 .NETの各マネージスレッドには、スタック用に1 MBのメモリ割り当てが必要です。 IOその最後の5秒で、1秒あたり500リクエストの負荷で、スレッドプールに2,500スレッドが必要です。つまり、スレッドのスタックに2.5 GBのメモリが必要です。コンテキスト切り替えの問題が発生すると、マシンのパフォーマンスに大きな負荷がかかります(Webアプリケーションだけでなく、マシン上のすべてのサービスに影響します)。実行中のスレッドの数がマシン上の論理CPUの数(通常16以下)に等しいときに最高の効率が得られることに注意してください。
そのため、スレッドプールのサイズを大きくすることがソリューションであり、10年の間(Microsoftの自社製品でも)人々はそれを行ってきましたが、メモリとCPU使用率の点で、スケーラビリティと効率性は劣ります。飢=を引き起こすIOレイテンシーの突然の増加の慈悲。C#5.0までは、非同期コードの複雑さは多くの人々にとって面倒の価値がありませんでした。 、非同期IOのスケーラビリティのメリットを享受し、同時に簡単なコードを作成できます。
詳細: http://msdn.Microsoft.com/en-us/library/ff647787.aspx "非同期呼び出しを使用して、WebサービスまたはリモートオブジェクトがWebサービス呼び出しの進行中に追加の並列処理を実行する機会ASP.NETスレッドプールのスレッドを使用して発信Webサービス呼び出しが行われるため、可能な場合は、Webサービスへの同期(ブロック)呼び出しを避けます。他の着信要求の処理に使用可能なスレッド。」
SynchronizationContext
のASP.NET実装によって管理されます。 SynchronizationContext
の詳細については、 私のMSDN記事 -ASP.NETのSynchronizationContext
の仕組みとawait
がSynchronizationContext
を使用する方法について説明しています。ASP.NET非同期処理は、非同期/待機の前に可能でした-非同期ページを使用し、WebClient
などのEAPコンポーネントを使用できます(イベントベースの非同期プログラミングは、SynchronizationContext
に基づく非同期プログラミングのスタイルです)。 Async/awaitもSynchronizationContext
を使用しますが、muchより簡単な構文があります。
スレッドプールを、あなたがyour workを行うために使用したワーカーのセットとして想像してください。ワーカーは高速で実行されますcpyourの手順。
今、あなたの仕事はたまたま他の遅い男の仕事に依存しています。遅い人はディスクまたはネットワークです。たとえば、作業には2つの部分があります。1つは実行する必要がある部分before遅い実行者の作業、もう1つは実行する必要がある部分after遅い実行者の作業です。
仕事をするように労働者にどのようにアドバイスしますか?各ワーカーに、「この最初の部分を実行してから、遅い男が完了するまで待ってから、2番目の部分を実行してください」と言いますか?すべての人がその遅い男を待っているようで、新しい顧客を満足させることができないので、あなたの労働者の数を増やしますか?番号!
代わりに、各ワーカーに最初の部分を実行するように依頼し、遅い人に戻ってきて、完了したらメッセージをキューにドロップするように依頼します。各ワーカー(またはおそらくワーカーの専用サブセット)に、キュー内の完了メッセージを探して作業の2番目の部分を実行するように指示します。
スマートカーネル上記で言及しているのは、低速ディスクとネットワークのキューを維持するオペレーティングシステムの機能です。IO完了メッセージ。