ASP.NET MVCで非同期操作を実行する.NET 4のThreadPoolのスレッドを使用する


158

この質問の後、ASP.NET MVCで非同期操作を使用するときに快適になります。それで、私はそれに2つのブログ投稿を書きました:

ASP.NET MVCでの非同期操作に関する誤解が多すぎます。

私はいつもこの文章を聞きます:操作が非同期で実行されれば、アプリケーションはより良くスケールできます

そして、私はこの種の文章もたくさん聞いた:大量のトラフィックがある場合は、クエリを非同期に実行しない方がいいかもしれません-1つのリクエストを処理するために2つの追加スレッドを消費すると、他の受信リクエストからリソースが奪われます。

この2つの文は一貫していないと思います。

ASP.NETでスレッドプールがどのように機能するかについてはあまり情報がありませんが、スレッドプールのスレッドのサイズには制限があることを知っています。したがって、2番目の文はこの問題に関連している必要があります。

また、ASP.NET MVCの非同期操作で.NET 4のThreadPoolのスレッドが使用されているかどうかを知りたいのですが。

たとえば、AsyncControllerを実装すると、アプリはどのように構成されますか?大量のトラフィックが発生した場合、AsyncControllerを実装することは良い考えですか?

この黒いカーテンを目の前に置いて、ASP.NET MVC 3(NET 4)での非同期に関する取引について説明してくれる人はいますか?

編集:

以下のドキュメントをほぼ数百回読みましたが、主要な取引は理解していますが、一貫性のないコメントが多すぎるため、混乱しています。

ASP.NET MVCでの非同期コントローラーの使用

編集:

以下のようなコントローラアクションがあるとしましょう(AsyncControllerただし、実装ではありません)。

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

ご覧のように、私は作戦を発動し、それを忘れます。その後、完了するのを待たずにすぐに戻ります。

この場合、これはスレッドプールのスレッドを使用する必要がありますか?もしそうなら、それが完了した後、そのスレッドはどうなりますか?DOESは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

    }
}

答えはわかりませんが、非同期とマルチスレッドは別物です。したがって、非同期処理を行うスレッドの数を固定することが可能です。たとえば、あるページがI / Oをブロックする必要がある場合、別のページが同じスレッドで実行される可能性があります。つまり、これらのステートメントの両方が真である可能性があります。非同期は物事をより速くすることができますが、スレッドが多すぎることは問題です。
Chris Chilvers、2012年

@ChrisChilversはい、非同期操作ではマルチスレッドは必ずしも必要ではありません。私はすでにそれを理解していましたが、私が知る限り、私にはその上にコントローラーはないと思います。AsyncControllerは、私の観点からは必要なスレッド数をスピンアップしますが、それについても不明です。WPFなどのデスクトップアプリにもスレッドプールの概念はありますか?この種のアプリではスレッド数は問題ではないと思います。
tugberk 2012年


問題(したがって不整合)は、2番目のステートメントが多くのスレッドを意味するときに非同期を使用することだと思います。これは、asp.netが非同期ページを実装し、特定の実装が問題を混乱させたためである可能性があります(問題を引き起こす機能の名前は非同期ページであるため)、特定の実装についてはわかりません。したがって、「多くのスレッド」を意味するか、「asp.netバージョンX内の非同期ページ」を意味するかのどちらかです。将来のバージョンでは実装が変更される可能性があるためです。または、スレッドプールを使用してページ内で非同期を実行することを意味します。
Chris Chilvers、2012年

@ChrisChilversああ、男!これらのコメントの後、私はもっと混乱します:s
tugberk '05年

回答:


177

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メソッド内で発生します。内部にブロック操作がある場合、ワーカースレッドをブロックしているため、非同期コントローラーの目的全体を完全に浪費しています(このアクションの本体はスレッドプールから取得されたスレッドで実行されることに注意してください)。

それで、あなたが尋ねるかもしれない非同期コントローラーの本当の利点をいつ利用できますか?

IHOは、I / O集中型の操作(データベースやリモートサービスへのネットワーク呼び出しなど)があるときに最も多く得られます。CPUを集中的に使用する操作がある場合、非同期アクションはあまりメリットをもたらしません。

では、なぜI / O集約型の操作からメリットを得ることができるのでしょうか。I / O完了ポートを使用できるからです。操作全体の実行中にサーバー上のスレッドやリソースを消費しないため、IOCPは非常に強力です。

それらはどのように機能しますか?

WebClient.DownloadStringAsyncメソッドを使用してリモートWebページのコンテンツをダウンロードするとします。このメソッドを呼び出すと、オペレーティングシステム内にIOCPが登録され、すぐに戻ります。リクエスト全体の処理中、サーバーでスレッドは消費されません。すべてがリモートサーバーで発生します。これには多くの時間がかかる可能性がありますが、ワーカースレッドを危険にさらしていないので気にする必要はありません。応答が受信されると、IOCPが通知され、スレッドプールからスレッドが引き出され、このスレッドでコールバックが実行されます。しかし、ご覧のとおり、プロセス全体を通して、スレッドを独占していません。

FileStream.BeginRead、SqlCommand.BeginExecuteなどのメソッドについても同様です。

複数のデータベース呼び出しの並列化についてはどうですか?4つのブロッキングデータベースコールを順番に実行する同期コントローラーアクションがあるとします。各データベースの呼び出しに200ミリ秒かかる場合、コントローラーアクションの実行に約800ミリ秒かかることは簡単に計算できます。

これらの呼び出しを順次実行する必要がない場合、それらを並列化するとパフォーマンスが向上しますか?

それは大きな問題であり、答えるのは容易ではありません。たぶんそうだけどたぶん違う。これらのデータベース呼び出しの実装方法に完全に依存します。前述のように非同期コントローラーとI / O完了ポートを使用すると、ワーカースレッドを独占することがなくなるため、このコントローラーアクションや他のアクションのパフォーマンスも向上します。

一方、それらを適切に実装しない場合(スレッドプールからのスレッドで実行されるブロッキングデータベース呼び出しを使用)、基本的にこのアクションの合計実行時間は約200ミリ秒に短縮されますが、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
本当に詳細、ありがとう!メソッドSystem.Threading.Task内で4つの非同期タスク()が実行されていると仮定しIndexAsyncます。これらの操作の中で、サーバーに対してdb呼び出しを行っています。つまり、それらはすべてI / O集約型の操作です。その場合、4つの別個のスレッドを作成しますか(またはスレッドプールから4つの別個のスレッドを取得しますか)?私がマルチコアマシンを持っていると仮定すると、それらも並列に実行されるでしょう?
tugberk 2012年

10
@tugberk、データベース呼び出しはI / O操作ですが、それらはすべてそれらを実装する方法に依存します。このようなブロッキングデータベースコールを使用している場合SqlCommand.ExecuteReader、これはブロッキングコールであるため、すべてを無駄にしています。この呼び出しが実行されるスレッドをブロックしています。このスレッドがたまたまプールからのスレッドである場合、それは非常に悪いです。I / O完了ポートを使用する場合にのみメリットがありますSqlCommand.BeginExecuteReader。どうしてもIOCPを使用しない場合は、非同期コントローラーを使用しないでください。アプリケーションの全体的なパフォーマンスが向上するだけでなく、損害も大きくなります。
Darin Dimitrov、2012年

1
まあ、ほとんどの場合、EFコードを最初に使用します。収まるかどうかはわかりません。私が一般的に行っていることを示すサンプルを載せました。質問を更新しました。ご覧いただけますか?
tugberk

3
@tugberk、それらを並行して実行しているため、実行の合計時間は、それらを連続して実行する場合に比べて短くなります。しかし、それらを実行するには、ワーカースレッドを使用します。まあ実際にはEFは怠惰なので、_context.Foo実際には何も実行していません。表現ツリーを構築しています。これには非常に注意してください。クエリの実行は、結果セットの列挙を開始したときにのみ延期されます。そして、これがビューで発生した場合、パフォーマンスに壊滅的な影響を与える可能性があります。熱心にEFクエリを実行するに.ToList()は、最後に追加します。
Darin Dimitrov

2
@tugberkでは、Webサイトで複数のユーザーを並行してシミュレートして、高負荷での動作を確認するための負荷テストツールが必要になります。ミニプロファイラーはWebサイトの負荷をシミュレートできません。ADO.NETクエリを確認および最適化し、多数のユーザーがアクセスしている実際の状況でサイトがどのように動作するかを確認する必要があるときに役に立たない単一の要求をプロファイルするのに役立ちます。
Darin Dimitrov、2012年

49

はい-すべてのスレッドはスレッドプールから取得されます。MVCアプリはすでにマルチスレッド化されており、リクエストが受信されると、新しいスレッドがプールから取得され、リクエストのサービスに使用されます。そのスレッドは、要求が完全に処理されて完了するまで(他の要求から)「ロック」されます。プールに使用可能なスレッドがない場合、要求はスレッドが使用可能になるまで待機する必要があります。

非同期コントローラーがある場合でも、プールからスレッドを取得しますが、要求を処理している間、何かが発生するのを待っている間(そのスレッドは別の要求に渡される可能性があります)、元の要求にスレッドが必要なときに、スレッドを放棄できます。再び、プールから1つ取得します。

違いは、長時間実行されるリクエスト(スレッドが何かからの応答を待機している)が多数ある場合、プールからのスレッドが不足して基本的なリクエストでさえも処理できる可能性があることです。非同期コントローラーがある場合、それ以上スレッドはありませんが、待機中のスレッドはプールに返され、他の要求を処理できます。

ほぼ現実の例は...バスに乗るように考えて、そこに、まず、上の取得支払うに乗るのを待って5人だし、(ドライバがその要求を処理した)、あなたが乗る座る(ドライバがサービスしていますあなたの要求)しかしあなたはあなたのお金を見つけることができません; ポケットにいじり回すと、ドライバーはあなたをあきらめて次の2人に(彼らの要求に応えて)着きます。あなたのお金が見つかると、ドライバーはあなたに再び対処し始めます(あなたの要求を完了します)-5番目の人はまで待つ必要がありますあなたは終わったが、あなたが奉仕を受ける途中である間に、3人目と4人目が出された。これは、ドライバーがプールからの唯一のスレッドであり、乗客が要求であることを意味します。ドライバーが2つある場合の動作を記述するのは複雑すぎましたが、想像できるでしょう...

非同期コントローラーがないと、後ろの乗客はお金を探す間年齢を待たなければならず、その間バスの運転手は何もしません。

したがって、結論は、多くの人が自分のお金がどこにあるかを知らない場合(つまり、ドライバーが求めたものに応答するのに長い時間が必要な場合)、非同期コントローラーは要求のスループットに役立ち、一部のプロセスを高速化できるということです。aysncコントローラーがなければ、誰もが前の人が完全に対処されるまで待機します。ただし、MVCでは単一のバスに多数のバスドライバーがあるため、非同期は自動的に選択されないことを忘れないでください。


8
とてもいいアナロジー。ありがとうございました。
ピッツバーグDBA 2012

説明が気に入りました。ありがとう
Omer Cansizoglu 2012年

it.Thankにあなたを説明するための優れた方法、
アナンドVyasさん

Darinの回答と組み合わせたあなたの回答は、非同期コントローラーの背後にあるメカニズム全体、それが何であるか、さらに重要なことは何がそうでないかを要約しています!
Nirman 2017

これは素晴らしいアナロジーですが、私の唯一の質問はこれです。あなたのアナロジーでポケットに手を入れている人は、アプリケーションでのある種の作業/処理です...それで、スレッドを解放するとどのようなプロセスが機能しますか?別にそれは別のスレッドですか?ここで何を得るのでしょうか?
トムケ

10

ここでは、2つのコンセプトがあります。まず、コードを並列実行して別のスレッドでコードをより高速に実行したり、ユーザーを待たせないようにコードをスケジュールしたりできます。あなたが持っていた例

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

2番目のカテゴリに属します。ユーザーは応答が速くなりますが、サーバーが同じ処理を実行してスレッドを処理する必要があるため、サーバーの総ワークロードは高くなります。

この別の例は次のとおりです。

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Make async web request to twitter with WebClient.DownloadString()
    });

    Task.Factory.StartNew(() => { 
        //Make async web request to facebook with WebClient.DownloadString()
    });


    //wait for both to be ready and merge the results

    return View();
}

リクエストは並行して実行されるため、ユーザーは、連続して実行される場合と同じくらい待つ必要はありません。ただし、スレッド待機中にも多くのスレッドでコードを実行するため、シリアルで実行する場合よりもここで多くのリソースを使用することを理解する必要があります。

これはクライアントのシナリオでは完全に問題ありません。また、同期実行時間の長いコードを新しいタスクでラップする(別のスレッドで実行する)こともよくあります。UIの応答性を維持するか、パラレライズして高速化します。ただし、スレッドは引き続き全期間使用されます。高負荷のサーバーでは、実際により多くのリソースを使用するため、これは逆効果になる可能性があります。これは人々があなたに警告したものです

ただし、MVCの非同期コントローラーには別の目標があります。ここでの要点は、スレッドが何もしない状態に置かれないようにすることです(スケーラビリティを損なう可能性があります)。呼び出すAPIに非同期メソッドがあるかどうかだけが問題になります。WebClient.DowloadStringAsync()のように。

ポイントは、同じまたは新しいスレッドを取得してリクエストを完了するコールバックを呼び出すWebリクエストが完了するまで、新しいリクエストを処理するためにスレッドを返すことができるということです。

非同期と並列の違いを理解していただければ幸いです。並列コードは、スレッドが置かれ、結果を待つコードと考えてください。非同期コードは、コードが完了したときに通知され、コードで作業を再開できるコードですが、その間、スレッドは他の作業を行うことができます。


6

アプリケーションは、できる操作は非同期で実行する場合、より良いスケーラビリティが、追加の操作にサービスを提供するために利用可能なリソースがある場合にのみ

非同期操作により、既存のアクションが進行中であるため、アクションがブロックされることがなくなります。ASP.NETには、複数の要求を並べて実行できる非同期モデルがあります。リクエストをキューに入れてFIFOで処理することは可能ですが、何百ものリクエストをキューに入れ、各リクエストの処理に100ミリ秒かかる場合、これは適切にスケーリングされません。

あなたは、トラフィックの膨大な量を持っている場合は、可能より良いオフ、非同期クエリを実行していないことが要求を処理するために追加のリソースが存在しない場合があるよう。予備のリソースがない場合、リクエストは強制的にキューに入れられ、指数関数的に長くかかるか、完全に失敗します。この場合、非同期オーバーヘッド(ミューテックスとコンテキスト切り替え操作)は何も提供しません。

ASP.NETに関する限り、選択肢はありません。非同期モデルを使用します。これは、サーバークライアントモデルにとって意味があるためです。すべてのリクエスト間で共有されるリソースを管理しようとしない限り、より良いスケーリングを試みるために非同期パターンを使用する独自のコードを内部で書く場合、それらはすでにラップされているため、実際には改善は見られません。他のものをブロックしない非同期プロセスで。

最終的に、システムのボトルネックの原因を実際に確認するまでは、すべて主観的です。非同期パターンが役立つ場所が(キューに入れられたリソースのブロックを防ぐことにより)明らかな場合があります。最終的には、システムの測定と分析のみが、どこで効率を上げることができるかを示すことができます。

編集:

あなたの例では、Task.Factory.StartNew呼び出しは.NETスレッドプールの操作をキューに入れます。スレッドプールスレッドの性質は再利用されます(多数のスレッドを作成/破棄するコストを回避するため)。操作が完了すると、スレッドは解放されてプールに戻され、別のリクエストで再利用されます(ガベージコレクターは、操作でいくつかのオブジェクトを作成しない限り実際には関与しません。その場合、オブジェクトは通常どおり収集されますスコーピング)。

ASP.NETに関する限り、ここでは特別な操作はありません。ASP.NET要求は、非同期タスクに関係なく完了します。唯一の懸念は、スレッドプールが飽和している(つまり、現在要求にサービスを提供できるスレッドがなく、プールの設定でこれ以上のスレッドを作成できない)場合です。その場合、要求は、開始を待機してブロックされます。プールスレッドが利用可能になるまでのタスク


ありがとう!回答を読んだ後、コードサンプルを使用して質問を編集しました。見ていただけますか?
tugberk 2012年

あなたは私のためにそこに魔法の文章があります:Task.Factory.StartNew呼び出しは.NETスレッドプールの操作をキューに入れます。。このコンテキストでは、どちらが正しいですか。1-)新しいスレッドを作成し、完了したら、そのスレッドはスレッドプールに戻り、そこで再利用されるのを待ちます。2-)スレッドプールからスレッドを取得し、そのスレッドはスレッドプールに戻り、そこで再利用されるのを待ちます。3-)最も効率的なアプローチを採用し、どちらでも実行できます。
tugberk 2012年

1
スレッドプールは、必要に応じてスレッドを作成し、使用されていないスレッドをリサイクルします。正確な動作はCLRのバージョンによって異なります。あなたはここでそれについての具体的な情報を見つけることができますmsdn.microsoft.com/en-us/library/0ka9477y.aspx
ポール・ターナー

それは今私の心の中で形を作り始めています。つまり、CLRはスレッドプールを所有します。たとえば、WPFアプリケーションにもスレッドプールの概念があり、そこにもプールがあります。
tugberk 2012年

1
スレッドプールは、CLR内で独自のものです。プールを「認識」している他のコンポーネントは、独自のスレッドを作成して破棄するのではなく、スレッドプールスレッド(適切な場合)を使用することを示しています。スレッドの作成または破棄は比較的コストのかかる操作なので、プールを使用すると、実行時間の短い操作の効率が大幅に向上します。
Paul Turner

2

はい、スレッドプールのスレッドを使用します。実際には、MSDNからの非常に優れたガイドがあり、すべての質問などに取り組みます。私はそれが過去に非常に役立つことがわかりました。見てみな!

http://msdn.microsoft.com/en-us/library/ee728598.aspx

その間、非同期コードについて聞くコメント+提案は、細かく理解する必要があります。手始めに、何かを非同期にしても、必ずしもスケーリングが向上するわけではなく、場合によってはアプリケーションのスケーリングが悪化する可能性があります。「膨大な量のトラフィック...」についてあなたが投稿した他のコメントも、特定の状況でのみ正しいです。それは実際には、あなたの操作が何をしているか、そしてそれらがシステムの他の部分とどのように相互作用するかに依存します。

要するに、多くの人々は非同期について多くの意見を持っていますが、彼らは文脈外では正しくないかもしれません。私はあなたの正確な問題に焦点を合わせ、非同期コントローラーなどが実際にアプリケーションで処理するものを確認するために基本的なパフォーマンステストを行います


私はそのドキュメントを何百回も読んだことがありますが、それでも非常に混乱しています(多分問題は私にあります)。見回すと、私の質問でわかるように、ASP.NET MVCでの非同期に関する一貫性のないコメントがたくさんあります。
tugberk 2012年

最後の文について:コントローラーアクション内で、データベースに個別に5回クエリを実行しました(私はしなければなりませんでした)、すべてに約400 msかかっていました。次に、AsyncControllerを実装し、それらを並行して実行しました。応答時間は劇的に約に短縮されました。200ミリ秒。しかし、それが作成するスレッドの数、スレッドの処理後にそれらのスレッドに何が起こるか、完了後すぐに実行されてGCクリーンアップされ、アプリでメモリリークが発生しないようになります。その部分についてのアイデア。
tugberk 2012年

デバッガを接続して調べます。
AR、

0

まず、MVCではなく、スレッドプールを管理するIISです。したがって、MVCまたはASP.NETアプリケーションに送信される要求は、スレッドプールで維持されているスレッドから提供されます。アプリを非同期にして初めて、彼は別のスレッドでこのアクションを呼び出し、すぐにスレッドを解放して、他のリクエストを受け取ることができるようにします。

MVCでスレッドスタベーションがどのように発生し、MVCを使用して最小化されるかを示す詳細ビデオ(http://www.youtube.com/watch?v=wvg13n5V0V0/ "MVC Asynch controllers and thread starvation")で同じことを説明しました非同期コントローラー。私はまた、perfmonを使用して要求キューを測定しました。これにより、MVC非同期で要求キューがどのように減少し、同期操作でどのように最悪であるかを確認できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.