70

ここオランダで開催された Techdays で、Steve Sanderson がC#5、ASP.NET MVC 4、および非同期 Webについてプレゼンテーションを行いました。

彼は、リクエストが完了するまでに時間がかかると、スレッドプールのすべてのスレッドがビジー状態になり、新しいリクエストを待たなければならなくなると説明しました。サーバーは負荷を処理できず、すべてが遅くなります。

彼は次に、非同期 Web リクエストを使用するとパフォーマンスが向上することを示しました。これは、作業が別のスレッドに委任され、スレッドプールが新しい受信リクエストに迅速に応答できるためです。彼はこれをデモし、50 の同時リクエストが最初に 50 * 1 秒かかったことを示しましたが、非同期動作では合計で 1.2 秒しかかかりませんでした。

しかし、これを見た後、私はまだいくつかの質問があります。

  1. より大きなスレッドプールを使用できないのはなぜですか? async/await を使用して別のスレッドを起動するのが遅くなっているのではなく、最初からスレッドプールを増やしていませんか? 私たちが実行しているサーバーが突然スレッドを増やしたり、何かを取得したりするようなものではありませんか?

  2. ユーザーからの要求は、非同期スレッドが終了するのをまだ待っています。プールからのスレッドが何か他のことをしている場合、「UI」スレッドはどのようにビジーに保たれますか? Steve は、「何かがいつ終了したかを知るスマート カーネル」について言及しました。これはどのように作動しますか?

4

3 に答える 3

68

これは非常に良い質問です。非同期 IO がなぜ重要なのかを理解するには、この質問を理解することが重要です。新しい async/await 機能が C# 5.0 に追加された理由は、非同期コードの記述を簡素化するためです。サーバーでの非同期処理のサポートは新しいものではありませんが、ASP.NET 2.0 から存在しています。

Steve が示したように、同期処理では、ASP.NET (および WCF) の各要求は、スレッド プールから 1 つのスレッドを取得します。彼がデモを行った問題は、「スレッド プールの枯渇」と呼ばれるよく知られた問題です。サーバーで同期 IO を行うと、IO の間、スレッド プール スレッドはブロックされたままになります (何もしません)。スレッド プール内のスレッド数には制限があるため、負荷がかかると、すべてのスレッド プール スレッドが IO を待ってブロックされ、要求がキューに入れられ始め、応答時間が長くなる可能性があります。すべてのスレッドが IO の完了を待っているため、CPU 占有率が 0% に近いことがわかります (応答時間は非常に長くなりますが)。

あなたが求めていること (なぜ、より大きなスレッドプールを使用できないのですか? ) は非常に良い質問です。実際のところ、スレッド プールの枯渇の問題をこれまでほとんどの人が解決してきた方法は、スレッド プールのスレッド数を増やすことだけでした。Microsoft の一部のドキュメントでは、スレッド プールの枯渇が発生する可能性がある状況の修正として、それが示されています。これは許容できる解決策であり、C# 5.0 までは、コードを完全に非同期になるように書き直すよりもはるかに簡単でした。

ただし、このアプローチにはいくつかの問題があります。

  • すべての状況で機能する値はありません。必要になるスレッド プール スレッドの数は、IO の期間とサーバーの負荷に直線的に依存します。残念ながら、IO レイテンシはほとんど予測できません。例を次に示します。たとえば、ASP.NET アプリケーションでサード パーティの Web サービスに対して HTTP 要求を行い、完了するまでに約 2 秒かかるとします。スレッド プールの枯渇が発生したため、スレッド プールのサイズをたとえば 200 スレッドに増やすことにすると、正常に動作し始めます。問題は、おそらく来週、Web サービスに技術的な問題が発生し、応答時間が 10 秒に増加することです。スレッドがブロックされる時間が 5 倍長くなるため、スレッド プールの枯渇が突然戻ってきます。

  • スケーラビリティとパフォーマンス: 2 番目の問題は、それを行う場合でも、要求ごとに 1 つのスレッドを使用することです。スレッドは高価なリソースです。.NET の各マネージ スレッドには、スタック用に 1 MB のメモリ割り当てが必要です。IO が 5 秒間続き、1 秒あたり 500 リクエストの負荷がかかる Web ページの場合、スレッド プールに 2,500 のスレッドが必要になります。つまり、何もしないスレッドのスタック用に 2.5 GB のメモリが必要になります。次に、コンテキスト切り替えの問題が発生し、マシンのパフォーマンスに大きな負担がかかります (Web アプリケーションだけでなく、マシン上のすべてのサービスに影響します)。Windows は、待機中のスレッドを無視するという点でかなりうまく機能しますが、そのような多数のスレッドを処理するようには設計されていません。

したがって、スレッド プールのサイズを大きくすることは解決策の 1 つであり、人々はそれを 10 年間 (Microsoft 自身の製品でさえも) 行ってきました。飢餓を引き起こす IO レイテンシーの急激な増加の慈悲。C# 5.0 までは、非同期コードの複雑さは、多くの人にとって面倒なことではありませんでした。async/await はすべてを変更します。非同期 IO のスケーラビリティの恩恵を受け、同時に単純なコードを記述できます。

詳細: http://msdn.microsoft.com/en-us/library/ff647787.aspx " Web サービス呼び出しの進行中に追加の並列処理を実行する機会がある場合は、非同期呼び出しを使用して Web サービスまたはリモート オブジェクトを呼び出します。可能な場合は、Web サービスへの同期 (ブロッキング) 呼び出しを避けてください。発信 Web サービス呼び出しは ASP.NET スレッド プールのスレッドを使用して行われるためです。ブロッキング呼び出しは、他の着信要求を処理するために使用できるスレッドの数を減らします

于 2012-02-27T13:56:21.213 に答える
32
  1. Async/awaitはスレッドに基づいていません。非同期処理に基づいています。ASP.NETで非同期待機を実行すると、要求スレッドがスレッドプールに返されるため、非同期操作が完了するまで、その要求を処理するスレッドはありません。リクエストのオーバーヘッドはスレッドのオーバーヘッドよりも低いため、これはasync/awaitがスレッドプールよりも適切にスケーリングできることを意味します。
  2. リクエストには、未処理の非同期操作の数が含まれています。このカウントは、のASP.NET実装によって管理されますSynchronizationContext。詳細についてSynchronizationContext、MSDNの記事を参照してください。ASP.NETのSynchronizationContext動作とawait使用方法について説明していますSynchronizationContext

ASP.NET非同期処理は、async / awaitの前に可能でした。非同期ページを使用したり、WebClient(イベントベースの非同期プログラミングはに基づく非同期プログラミングのスタイルですSynchronizationContext)などのEAPコンポーネントを使用したりできます。Async / awaitもを使用しますSynchronizationContextが、構文ははるかに簡単です。

于 2012-02-26T13:56:51.500 に答える
9

スレッドプールを、作業を行うために採用した一連のワーカーと想像してください。ワーカーは、コードに対して高速CPU命令を実行します。

あなたの仕事はたまたま別の遅い人の仕事に依存しています。遅い人はディスクまたはネットワークです。たとえば、作業には 2 つの部分があり、1 つの部分は遅い人の作業の前に実行する必要があり、もう 1 つの部分は遅い人の作業のに実行する必要があります。

従業員にあなたの仕事をするようにどのようにアドバイスしますか? 各作業員に、「この最初の部分を実行してから、あの遅い人が完了するまで待ってから、2 番目の部分を実行してください」と言いますか? 従業員全員がその遅い男を待っているように見え、新しい顧客を満足させることができないため、従業員の数を増やしますか? いいえ!

代わりに、各ワーカーに最初の部分を実行するように依頼し、遅い人に戻ってきて、完了したらキューにメッセージをドロップするように依頼します。各ワーカー (またはワーカーの専用サブセット) に、キュー内の完了メッセージを探して、作業の 2 番目の部分を実行するように指示します。

上記で言及しているスマートカーネルは、オペレーティングシステムが低速ディスクおよびネットワーク IO 完了メッセージのキューを維持する機能です。

于 2014-08-11T19:49:05.623 に答える