[解決済み] ASP.NET MVCの非同期操作では、.NET 4のThreadPoolからスレッドを使用しますか?
質問
<ブロッククオートこの質問の後、私は非同期を使用するときに快適になります。 の操作について説明します。そこで、そのことについて2つのブログ記事を書きました。
- C# 5.0とASP.NET MVC Webアプリケーションにおけるタスクベースの非同期プログラミングについての私の考え
- ASP.NET MVC 4のタスクベース非同期プログラミングモデル(TAP)による非同期データベースコール
ASP.NET MVCの非同期操作について、私の中で誤解が多すぎる。
いつもこんな文章を耳にします。 アプリケーションは、操作を非同期で実行した方がスケールが良くなります。
そして、このような文章もよく耳にしました。 1つのリクエストに対応するために2つのスレッドを余分に消費すると、他のリクエストからリソースが奪われてしまうからです。
この2つの文章は矛盾していると思うのですが。
ASP.NETでthreadpoolがどのように動作するかについてはあまり情報を持っていませんが、threadpoolにはスレッドのサイズに制限があることは知っています。だから、2番目の文章はこの問題に関連しているはずです。
そして、ASP.NET MVCの非同期操作が.NET 4のThreadPoolからスレッドを使用するかどうかを知りたいのですが?
例えば、AsyncControllerを実装した場合、アプリの構造はどうなるのでしょうか?膨大なトラフィックが発生した場合、AsyncControllerを実装するのは良いアイデアなのだろうか?
この黒い幕を取り払い、ASP.NET MVC 3 (NET 4)での非同期について説明してくれる人はいないのだろうか。
編集
私はこの下の文書を何百回となく読み、大筋は理解したのですが、あまりにも矛盾したコメントが多いので、未だに混乱しているのです。
編集する
以下のようなコントローラアクションがあるとします。
AsyncController
とはいえ)。
public ViewResult Index() {
Task.Factory.StartNew(() => {
//Do an advanced looging here which takes a while
});
return View();
}
このように、私はあるオペレーションを起動し、そのことを忘れています。そして、完了を待たずにすぐに戻ってくる。
この場合、threadpoolからスレッドを使用する必要があるのでしょうか?もしそうなら、それが完了した後、そのスレッドはどうなるのでしょうか?どうなんでしょう?
GC
が入ってきて、完了した直後に掃除してくれるのでしょうか?
編集する。
Darin さんの回答に対して、データベースと通信する非同期コードのサンプルを示します。
public class FooController : AsyncController {
//EF 4.2 DbContext instance
MyContext _context = new MyContext();
public void IndexAsync() {
AsyncManager.OutstandingOperations.Increment(3);
Task<IEnumerable<Foo>>.Factory.StartNew(() => {
return
_context.Foos;
}).ContinueWith(t => {
AsyncManager.Parameters["foos"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
Task<IEnumerable<Bars>>.Factory.StartNew(() => {
return
_context.Bars;
}).ContinueWith(t => {
AsyncManager.Parameters["bars"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
Task<IEnumerable<FooBar>>.Factory.StartNew(() => {
return
_context.FooBars;
}).ContinueWith(t => {
AsyncManager.Parameters["foobars"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
}
public ViewResult IndexCompleted(
IEnumerable<Foo> foos,
IEnumerable<Bar> bars,
IEnumerable<FooBar> foobars) {
//Do the regular stuff and return
}
}
解決方法は?
ここに 秀逸な記事 ASP.NETの非同期処理(非同期コントローラは基本的にこれを表しています)をより理解するために一読することをお勧めします。
まず、標準的な同期型アクションを考えてみましょう。
public ActionResult Index()
{
// some processing
return View();
}
このアクションにリクエストがあると、スレッドプールからスレッドが引き出され、このアクションの本体がこのスレッドで実行されます。したがって、このアクション内部の処理が遅い場合、このスレッドを処理全体にわたってブロックすることになるので、このスレッドを他のリクエストの処理に再利用することはできません。リクエストの実行が終了すると、このスレッドはスレッドプールに戻されます。
では、非同期パターンの例を見てみましょう。
public void IndexAsync()
{
// perform some processing
}
public ActionResult IndexCompleted(object result)
{
return View();
}
Index アクションにリクエストが送られると、スレッドプールから スレッドが引き出され、ボディに
IndexAsync
メソッドが実行されます。このメソッド本体の実行が終了すると、スレッドはスレッドプールに戻される。次に、標準の
AsyncManager.OutstandingOperations
非同期処理の完了を合図すると、スレッドプールから別のスレッドが引き出され、そのスレッドプールにある
IndexCompleted
アクションが実行され、その結果がクライアントにレンダリングされます。
つまり、このパターンでわかることは、1つのクライアントのHTTPリクエストを2つの異なるスレッドで実行することができるということです。
さて、面白いのは
IndexAsync
メソッドを使用します。このメソッドの中でブロックする操作を行うと、ワーカスレッドをブロックしてしまうので、非同期コントローラの目的を完全に無駄にしてしまいます (このアクションの本体は、スレッドプールから取得したスレッドで実行されることを覚えておいてください)。
では、いつになったら非同期コントローラを本当の意味で活用できるのでしょうか?
IMHOでは、I/O集中型の操作(データベースやリモートサービスへのネットワーク呼び出しなど)を行う場合に、最も大きな利益を得ることができると考えています。CPUに負荷のかかる操作の場合、非同期アクションはあまり利益をもたらさないでしょう。
では、なぜI/O集中型の操作でメリットが得られるのでしょうか。それは I/O 補完ポート . IOCPは、全体の操作の実行中にサーバー上のスレッドやリソースを消費しないので、非常に強力です。
どのように動作するのですか?
例えば、リモートのウェブページのコンテンツを WebClient.DownloadStringAsync メソッドを使用します。このメソッドを呼び出すと、オペレーティングシステム内にIOCPが登録され、即座にリターンします。リクエスト全体の処理中、あなたのサーバーではスレッドが消費されません。すべてはリモートサーバーで行われます。これには多くの時間がかかりますが、ワーカスレッドを危険にさらすことはないので気にすることはありません。レスポンスを受信すると、IOCP がシグナルされ、スレッドプールからスレッドが引き出され、コールバックはこのスレッドで実行されます。しかし、ご覧のように、このプロセス全体を通して、どのスレッドも独占していません。
FileStream.BeginRead や SqlCommand.BeginExecute などのメソッドも同様です。
複数のデータベース呼び出しを並列化する場合はどうでしょうか。例えば、同期コントローラのアクションで、4つのブロッキングデータベース呼び出しを順番に実行したとします。各データベース呼び出しに200msかかるとすると、コントローラアクションの実行におよそ800msかかることは容易に計算できます。
これらの呼び出しを順次実行する必要がない場合、並列化することでパフォーマンスを向上させることは可能でしょうか?
これが大きな疑問で、簡単に答えられるものではありません。イエスかもしれないし、ノーかもしれない。それは、データベースの呼び出しをどのように実装するかに完全に依存します。前述したように非同期コントローラとI/Oコンプリーションポートを使用すれば、ワーカスレッドを独占しないので、このコントローラアクションと他のアクションのパフォーマンスを向上させることができます。
一方、これらの実装が不適切な場合(スレッドプールのスレッドでデータベース呼び出しをブロックする)、基本的にこのアクションの合計実行時間はおよそ200msに低下しますが、4つのワーカスレッドを消費するので、他のリクエストのパフォーマンスが低下し、それらを処理するスレッドがプールにないために飢餓状態になる可能性があります。
このように、非同期コントローラは非常に難しいもので、もしアプリケーションに対して大規模なテストを行う準備ができていないのであれば、非同期コントローラを実装しないようにしましょう。例えば、標準的な同期コントローラのアクションがアプリケーションのボトルネックになっていることを確認した場合(もちろん、広範囲にわたる負荷テストと測定を行った後)です。
では、あなたの例を考えてみましょう。
public ViewResult Index() {
Task.Factory.StartNew(() => {
//Do an advanced looging here which takes a while
});
return View();
}
Index アクションのリクエストを受けると、その本体を実行するためにスレッドプールからスレッドが引き出されますが、その本体は、以下のメソッドを使用して新しいタスクをスケジュールするだけです。 TPL . そのため、アクションの実行は終了し、スレッドはスレッドプールに戻される。ただし TPL は、スレッドプールのスレッドを使用して処理を実行します。そのため、元のスレッドがスレッドプールに戻されたとしても、このプールから別のスレッドを引き出してタスク本体を実行したことになります。つまり、貴重なプールから2つのスレッドを危険にさらしているわけです。
では、次のように考えてみましょう。
public ViewResult Index() {
new Thread(() => {
//Do an advanced looging here which takes a while
}).Start();
return View();
}
この場合、手動でスレッドを生成しています。この場合、Index アクションの本体の実行に少し時間がかかるかもしれません (新しいスレッドを生成するのは、既存のプールからスレッドを引き出すよりもコストがかかるためです)。しかし、高度なロギング操作の実行は、プールの一部ではないスレッドで行われます。したがって、他のリクエストに対応するために空いているプールからのスレッドを危険にさらすことはありません。
関連
-
[解決済み】プログラム実行中に1秒待つ
-
[解決済み】Visual studio 2019がデバッグ時にフリーズする件
-
[解決済み] 他のスレッドからGUIを更新するにはどうすればよいですか?
-
[解決済み] C#で同期メソッドから非同期メソッドを呼び出すには?
-
[解決済み] .NETでapp.configやweb.configから設定を読み込む
-
[解決済み] ファイルアップロード ASP.NET MVC 3.0
-
[解決済み] ASP.NET MVC Frameworkで複数のサブミットボタンを処理する方法は?
-
[解決済み] ASP.NET MVCでenumからドロップダウンリストを作成するにはどうすればよいですか?
-
[解決済み] ASP.NET MVC - カスタムIIdentityまたはIPrincipalの設定
-
[解決済み] ASP.NET MVCでビューをコンパイルする
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】エラー。「戻り値を変更できません」 C#
-
[解決済み】プログラム実行中に1秒待つ
-
[解決済み】"The ConnectionString property has not been initialized "を修正する方法
-
[解決済み】「namespace x already contains a definition for x」エラーの修正方法は?VS2010にコンバートした後に発生しました。
-
[解決済み】バックスラッシュを含むパス文字列のエスケープシーケンスが認識されない件
-
[解決済み】取り消せないメンバはメソッドのように使えない?
-
[解決済み】Unity 「関連するスクリプトを読み込むことができません」「Win32Exception: システムは指定されたファイルを見つけることができません"
-
[解決済み】WSACancelBlockingCallの例外について
-
[解決済み】Linq 構文 - 複数列の選択
-
[解決済み】ユーザー設定値を別のユーザー設定値で設定する