113

私は、ASP.NET であっても、存続期間の短いバックグラウンド タスク (重要ではないものとしましょう) に ThreadPool を使用することがベスト プラクティスであると常に考えていましたが、そうではないことを示唆しているように思われるこの記事に出くわしました。引数は、ASP.NET 関連の要求を処理するために ThreadPool を残す必要があるということです。

だから、これまで私が小さな非同期タスクを行ってきた方法は次のとおりです。

ThreadPool.QueueUserWorkItem(s => PostLog(logEvent))

そして、この記事では代わりに、次のようにスレッドを明示的に作成することを提案しています。

new Thread(() => PostLog(logEvent)){ IsBackground = true }.Start()

最初の方法には、管理され制限されているという利点がありますが、(この記事が正しければ) バックグラウンド タスクが ASP.NET 要求ハンドラーを使用してスレッドを競合する可能性があります。2 番目の方法は ThreadPool を解放しますが、その代償として制限がなくなるため、多くのリソースを消費する可能性があります。

私の質問は、この記事のアドバイスは正しいですか?

サイトのトラフィックが多すぎて ThreadPool がいっぱいになった場合、帯域外に移行した方がよいでしょうか。それとも、ThreadPool がいっぱいになったということは、とにかくリソースの限界に達していることを意味します。自分のスレッドを開始しようとするべきではありませんか?

明確化: 私は、重要ではない小さな非同期タスク (リモート ロギングなど) の範囲で質問しているだけであり、別のプロセスを必要とする高価な作業項目ではありません (これらの場合、より堅牢なソリューションが必要になることに同意します)。

4

11 に答える 11

106

ここでの他の回答は、最も重要な点を省略しているようです:

低負荷のサイトで CPU を集中的に使用する操作をより高速に実行するために並列化しようとしている場合を除き、ワーカー スレッドを使用する意味はまったくありません。

これは、 によって作成されたフリー スレッドと、要求に応答するnew Thread(...)内のワーカー スレッドの両方に当てはまります。ThreadPoolQueueUserWorkItem

はい、確かに、あまりにも多くの作業項目をキューに入れることで、ASP.NET プロセスを枯渇させる可能性があります。ThreadPoolこれにより、ASP.NET がそれ以上の要求を処理できなくなります。この記事の情報はその点で正確です。に使用されるのと同じスレッド プールが、QueueUserWorkItem要求の処理にも使用されます。

しかし、実際にこの枯渇を引き起こすのに十分な作業項目をキューに入れている場合は、スレッド プールを枯渇させているはずです! 文字通り何百もの CPU 集中型の操作を同時に実行している場合、マシンが既に過負荷になっているときに、ASP.NET 要求を処理する別のワーカー スレッドを用意してもよいでしょうか? このような状況に陥っている場合は、完全に再設計する必要があります。

マルチスレッド コードが ASP.NET で不適切に使用されているのを見たり聞いたりすることがほとんどです。これは、CPU を集中的に使用する作業をキューに入れるためのものではありません。これは、I/O バウンドの作業をキューに入れるためのものです。また、I/O 作業を行う場合は、I/O スレッド (I/O 完了ポート) を使用する必要があります。

具体的には、使用しているライブラリ クラスでサポートされている非同期コールバックを使用する必要があります。これらのメソッドは常に非常に明確にラベル付けされています。それらは単語Beginとで始まりますEnd。、、、Stream.BeginReadなど。Socket.BeginConnect_WebRequest.BeginGetResponse

これらのメソッドを使用しますが、ASP.NET 要求に干渉しないThreadPoolIOCP を使用します。これらは、I/O システムからの割り込み信号によって「起動」できる特別な種類の軽量スレッドです。また、ASP.NET アプリケーションでは、通常、ワーカー スレッドごとに 1 つの I/O スレッドがあるため、すべての要求で 1 つの非同期操作をキューに入れることができます。これは文字通り数百回の非同期操作であり、パフォーマンスが大幅に低下することはありません (I/O サブシステムが維持できると仮定します)。それはあなたが必要とする以上のものです。

非同期デリゲートはこのようには機能しないことに注意してくださいThreadPool.QueueUserWorkItem。これを実行できるのは、.NET Framework ライブラリ クラスの組み込み非同期メソッドだけです。自分で行うこともできますが、複雑で少し危険であり、おそらくこの議論の範囲を超えています.

私の意見では、この質問に対する最良の答えは、 ASP.NET でまたはバックグラウンドインスタンスを使用しないことThreadPool Threadです。これは、Windows フォーム アプリケーションでスレッドをスピンアップするようなものではありません。この場合、UI の応答性を維持するためにスレッドを実行し、その効率性を気にする必要はありません。ASP.NET では、あなたの懸念はスループットであり、これらすべてのワーカー スレッドでコンテキストを切り替えると、を使用するかどうかに関係なく、スループットが完全に失わThreadPoolれます。

ASP.NET でスレッド コードを記述している場合は、既存の非同期メソッドを使用するように書き直すことができるかどうかを検討してください。それができない場合は、そのコードが本当に本当に必要かどうかを検討してください。バックグラウンドスレッドで実行する。ほとんどの場合、純利益を得るために複雑さを追加することになるでしょう。

于 2010-04-15T04:44:00.797 に答える
45

Microsoft の ASP.NET チームの Thomas Marquadt によると、ASP.NET ThreadPool (QueueUserWorkItem) を使用しても安全です。

記事から

Q) 私の ASP.NET アプリケーションが CLR ThreadPool スレッドを使用している場合、CLR ThreadPool を使用してリクエストを実行する ASP.NET を飢えさせることはありませんか? ..

A) 要約すると、ASP.NET のスレッドが枯渇する心配はありません。ここに問題があると思われる場合は、私に知らせてください。対処します。

Q) 自分のスレッド (新しいスレッド) を作成する必要がありますか? CLR ThreadPool を使用するため、これは ASP.NET に適しているのではないでしょうか。

A) しないでください。または、別の言い方をすれば、いいえ!!! あなたが本当に頭がいいなら、私よりずっと頭がいいなら、独自のスレッドを作成できます。そうでなければ、それについて考えることさえしないでください。新しいスレッドを頻繁に作成してはならない理由を次に示します。

  1. QueueUserWorkItem に比べて非常に高価です...ちなみに、CLR よりも優れた ThreadPool を作成できる場合は、Microsoft での仕事に応募することをお勧めします。
于 2011-05-17T21:41:55.007 に答える
4

ASP.NET での迅速で優先度の低い非同期作業の一般的な方法は、.NET スレッド プールを使用することだと思います。特に、リソースを制限したい場合など、トラフィックの多いシナリオではそうです。

また、スレッド化の実装は隠されています。独自のスレッドの生成を開始する場合は、それらも適切に管理する必要があります。できなかったと言っているわけではありませんが、なぜその車輪を再発明するのですか?

パフォーマンスが問題になり、スレッド プールが (データベース接続、発信ネットワーク接続、メモリ、ページ タイムアウトなどではなく) 制限要因であることを確認できる場合は、スレッド プールの構成を微調整して、より多くのワーカー スレッドを許可し、より多くのキューに入れられる要求を許可します。など

パフォーマンスの問題がない場合、ASP.NET 要求キューとの競合を減らすために新しいスレッドを生成することを選択するのは、古典的な時期尚早の最適化です。

理想的には、ロギング操作を行うために別のスレッドを使用する必要はありません。元のスレッドができるだけ早く操作を完了できるようにするだけで、MSMQ と別のコンシューマ スレッド/プロセスが登場します。これはより重く、実装する作業が増えることに同意しますが、ここでは耐久性が本当に必要です。共有メモリ内キューの揮発性は、すぐに歓迎されなくなります。

于 2009-08-25T05:32:09.750 に答える
4

Web サイトは、スポーン スレッドを回避するべきではありません。

通常、この機能を Windows サービスに移動してから通信します (私は MSMQ を使用して通信します)。

- 編集

ここで実装について説明しました: ASP.NET MVC Web アプリケーションでのキューベースのバックグラウンド処理

- 編集

これが単なるスレッドよりも優れている理由を拡張するには、次のようにします。

MSMQ を使用すると、別のサーバーと通信できます。複数のマシンにまたがるキューに書き込むことができるため、何らかの理由でバックグラウンド タスクがメイン サーバーのリソースを使いすぎていると判断した場合は、簡単にシフトすることができます。

また、実行しようとしていたタスク (メールの送信など) をバッチ処理することもできます。

于 2009-08-25T02:14:23.387 に答える
2

QueueUserWorkItem を使用し、ペストを回避するように新しいスレッドを作成しないようにする必要があります。同じ ThreadPool を使用するため ASP.NET を枯渇させない理由を説明するビジュアルについては、非常に熟練したジャグラーが両手を使って 6 本のボウリング ピンや剣などを飛行させているところを想像してください。独自のスレッドを作成することがなぜ悪いのかを視覚化するために、ラッシュアワーにシアトルで何が起こるかを想像してみてください。高速道路への入口ランプが頻繁に使用されているため、車両は信号を使用せずにすぐに交通に入ることができ、入り口の数は数秒ごとに 1 つに制限されます。 . 最後に、詳細な説明については、次のリンクを参照してください。

http://blogs.msdn.com/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx

ありがとう、トーマス

于 2010-04-15T04:04:44.557 に答える
1

記事が間違っていると言えます。大規模な .NET ショップを運営している場合は、 ThreadPoolドキュメントの 1 つのステートメントに基づいて、複数のアプリと複数の Web サイト (個別のアプリ プールを使用) でプールを安全に使用できます。

プロセスごとに 1 つのスレッド プールがあります。 スレッド プールのデフォルト サイズは、使用可能なプロセッサあたり 250 ワーカー スレッド、および 1000 I/O 完了スレッドです。スレッド プール内のスレッドの数は、SetMaxThreads メソッドを使用して変更できます。各スレッドはデフォルトのスタック サイズを使用し、デフォルトの優先度で実行されます。

于 2009-10-14T21:25:38.810 に答える
1

先週、職場で同様の質問を受けましたが、同じ回答をします。リクエストごとに Web アプリケーションをマルチスレッド化するのはなぜですか? Web サーバーは、タイムリーに多くの要求を提供するように大幅に最適化された素晴らしいシステムです (つまり、マルチスレッド)。Web 上のほぼすべてのページをリクエストするとどうなるか考えてみてください。

  1. あるページに対してリクエストが行われる
  2. Html が返されます
  3. Html は、クライアントにさらにリクエスト (js、css、画像など) を行うように指示します。
  4. 詳細情報が返されます

リモートロギングの例を挙げていますが、それはロガーの懸念事項です。メッセージをタイムリーに受信するには、非同期プロセスを用意する必要があります。Sam は、あなたのロガー (log4net) がすでにこれをサポートしているはずだとさえ指摘しています。

Sam は、CLR でスレッド プールを使用しても IIS のスレッド プールで問題が発生しないという点でも正しいです。ただし、ここで注意すべき点は、プロセスからスレッドを生成するのではなく、IIS スレッドプール スレッドから新しいスレッドを生成していることです。違いがあり、区別が重要です。

スレッドとプロセス

スレッドとプロセスはどちらも、アプリケーションを並列化する方法です。ただし、プロセスは独立した実行単位であり、独自の状態情報を含み、独自のアドレス空間を使用し、プロセス間通信メカニズム (通常はオペレーティング システムによって管理されます) を介してのみ相互に対話します。通常、アプリケーションは設計段階でプロセスに分割されます。重要なアプリケーション機能を論理的に分離することが理にかなっている場合、マスター プロセスは明示的にサブプロセスを生成します。つまり、プロセスはアーキテクチャ構造です。

対照的に、スレッドは、アプリケーションのアーキテクチャに影響を与えないコーディング構造です。1 つのプロセスに複数のスレッドが含まれる場合があります。プロセス内のすべてのスレッドは、同じ状態と同じメモリ空間を共有し、同じ変数を共有するため、互いに直接通信できます。

ソース

于 2009-10-15T16:08:29.760 に答える
1

その記事は正しくありません。ASP.NET には、ASP.NET 要求を処理するための独自のスレッド プール (マネージ ワーカー スレッド) があります。このプールは通常、数百のスレッドであり、プロセッサの数倍の小さな数である ThreadPool プールとは別のものです。

ASP.NET で ThreadPool を使用しても、ASP.NET ワーカー スレッドに干渉しません。ThreadPool を使用しても問題ありません。

メッセージをログに記録するためだけの単一のスレッドをセットアップし、プロデューサー/コンシューマー パターンを使用してログ メッセージをそのスレッドに渡すこともできます。その場合、スレッドは存続期間が長いため、ロギングを実行する単一の新しいスレッドを作成する必要があります。

メッセージごとに新しいスレッドを使用するのは、間違いなくやり過ぎです。

ロギングについてのみ話している場合の別の代替手段は、log4net のようなライブラリを使用することです。別のスレッドでログを処理し、そのシナリオで発生する可能性のあるすべてのコンテキストの問題を処理します。

于 2009-10-14T15:44:46.233 に答える
0

参照された記事 (C#feeds.com) に同意しません。新しいスレッドを作成するのは簡単ですが、危険です。1 つのコアで実行するアクティブなスレッドの最適な数は、実際には驚くほど少なく、10 未満です。スレッドがマイナー タスク用に作成されている場合、マシンがスレッドの切り替えに時間を浪費するのは非常に簡単です。スレッドは、管理が必要なリソースです。WorkItem 抽象化は、これを処理するために存在します。

ここでは、リクエストに使用できるスレッドの数を減らすことと、作成するスレッドが多すぎて効率的に処理できないこととの間にトレードオフがあります。これは非常に動的な状況ですが、スレッドの作成をプロセッサに任せるのではなく、(この場合はスレッド プールによって) 積極的に管理する必要があると思います。

最後に、この記事では、ThreadPool を使用する危険性についてかなり大雑把に述べていますが、それらを裏付ける具体的な何かが本当に必要です。

于 2010-02-03T19:00:12.747 に答える
0

IIS が同じ ThreadPool を使用して着信要求を処理するかどうかは、決定的な答えを得るのが難しいようであり、バージョンも変更されているようです。そのため、ThreadPool スレッドを過度に使用しないようにして、IIS で多数のスレッドを使用できるようにすることをお勧めします。一方、小さなタスクごとに独自のスレッドを作成するのは、悪い考えのように思えます。おそらく、ロギングにある種のロックがあるため、一度に進行できるスレッドは 1 つだけであり、残りのスレッドはスケジュールされたものとスケジュールされていないものを順番に取得するだけです (新しいスレッドを生成するオーバーヘッドは言うまでもありません)。基本的に、ThreadPool が回避するように設計された正確な問題に遭遇します。

メッセージを渡すことができる単一のログ スレッドをアプリに割り当てることは、妥当な妥協点のようです。アプリの速度を落とさないように、メッセージの送信ができるだけ速くなるように注意する必要があります。

于 2010-03-05T22:58:55.420 に答える