152

この質問の後、asyncを使うとき、それは私を快適にします   ASP.NET MVCでの操作だから、私はそれについて2つのブログ記事を書いた:

私は、ASP.NET MVCでの非同期操作について誤解しすぎています。

私はいつもこの文章を聞いています:操作が非同期的に実行される場合、アプリケーションはより優れた拡張性

そして私はこの種の文章もたくさん聞いた。大量のトラフィックがある場合は、クエリを非同期に実行しない方がよい場合があります。1つの要求を処理するために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();
}

あなたがここで見るように、私は操作を発射し、それを忘れます。その後、私はそれが完了するのを待たずにすぐに戻ります。

この場合、これは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

    }
}


  • 答えは定かではありませんが、非同期とマルチスレッドの違いは注目に値します。そのため、非同期処理で固定数のスレッドを持つことが可能です。あるページが、たとえばI / Oをブロックしなければならない場合、別のページが同じスレッドで実行される可能性があります。これは、これらのステートメントの両方が真実であることができる方法です、asyncは物事を速くすることができますが、多すぎるスレッドは問題です。 - Chris Chilvers
  • @ChrisChilversうん、非同期操作ではマルチスレッドは必ずしも必要ではありません。私はすでにそれを考え出しました、しかし私は私が言うことができる限り私はそれを制御しないと思います。 AsyncControllerは私の視点からそれが何個のスレッドを望むかをスピンアップしますが、それについても確信が持てません。 WPFのようなデスクトップアプリケーションにもスレッドプールの概念はありますか?スレッド数はこれらの種類のアプリでは問題にならないと思います。 - tugberk
  • ビデオを見てくださいJeff Richterによるスレッド - oleksii
  • 私は問題(そして矛盾)が多くのスレッドを意味するとき2番目のステートメントが非同期を使用することだと思います。これは、asp.netが非同期ページを実装したため、特定の実装が問題を混乱させたためである可能性があります(問題の原因となる機能の名前は非同期ページになるため)。特定の実装つまり、それらは「多数のスレッド」を意味するか、または「asp.netバージョンX内の非同期ページ」を意味します。将来のバージョンでは実装が変わるかもしれないので。あるいは、スレッドプールを使用してページ内で非同期を実行するということです。 - Chris Chilvers
  • ああ、男!私はこれらのコメントの後でもっと混乱しています:s - tugberk

6 답변


170

これが優秀品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アクションが実行され、結果がクライアントに表示されます。

したがって、このパターンでわかるのは、単一のクライアントHTTP要求が2つの異なるスレッドによって実行される可能性があるということです。

今興味深い部分はの中で起こりますIndexAsync方法。内部にブロック操作があると、ワーカースレッドをブロックしているので、非同期コントローラの目的を完全に無駄にしていることになります(このアクションの本体は、スレッドプールから引き出されたスレッドで実行されることに注意してください)。

では、非同期コントローラを実際に利用できるのはいつでしょうか。

私たちは、I / Oの集中的な操作(データベースやネットワークへのリモートサービスへの呼び出しなど)があるときに最も多く得ることができます。あなたがCPUに負荷をかける操作をしているならば、非同期アクションはあなたに大きな利益をもたらさないでしょう。

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

どうやって動くの?

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

FileStream.BeginRead、SqlCommand.BeginExecuteなどのメソッドでも同じことが言えます。

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

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

それは大きな問題です。答えるのは簡単ではありません。たぶんそうだけどたぶん違う。それは完全にあなたがそれらのデータベース呼び出しをどのように実装するかに依存するでしょう。前述のように非同期コントローラと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アクションの本体の実行には少し時間がかかることがあります(新しいスレッドを生成する方が既存のプールからスレッドを描画するよりも費用がかかるため)。しかし、高度なロギング操作の実行は、プールの一部ではないスレッド上で行われます。そのため、プールからのスレッドが他の要求を処理するために解放されたままになってしまう危険性はありません。


  • 本当に詳しく、ありがとうございます。 4つの非同期タスクがあると仮定しましょう(System.Threading.Task中を走るIndexAsync方法。これらの操作の中で、私たちはサーバーにdb呼び出しをしています。したがって、それらはすべてI / O集約型の操作です。その場合、4つの別々のスレッドを作成しますか(またはthread-poolから4つの別々のスレッドを取得しますか)。私がマルチコアマシンを持っていると仮定すると、それらは並行して走るでしょうね。 - tugberk
  • @tugberkでは、データベース呼び出しはI / O操作ですが、実装方法によって異なります。次のようなブロッキングデータベース呼び出しを使用するとします。SqlCommand.ExecuteReaderこれはブロッキングコールなので、あなたはすべてを無駄にしています。この呼び出しが実行されているスレッドをブロックしているため、このスレッドがプールからのスレッドである場合、それは非常に悪い状態です。 I / O完了ポートを使用している場合にのみ利点があります。SqlCommand.BeginExecuteReader。何をしてもIOCPを使用しない場合は、アプリケーションの全体的なパフォーマンスへの利益よりも多くの損害を与えるので、非同期コントローラを使用しないでください。 - Darin Dimitrov
  • さて、ほとんどの場合私は最初にEFコードを使います。収まるかどうかはわかりません。私は私が一般的にしていることを示すサンプルを提出しました。質問を更新しました。ご覧になれますか。 - tugberk
  • @tugberk、あなたはそれらを並行して実行しているので、実行の合計時間はそれらを順番に実行する場合よりも少なくなります。しかしそれらを実行するためにはワーカースレッドを使います。実際にはEFは怠惰なので、実行すると_context.Fooあなたは実際には何も実行していません。あなたは式ツリーを構築しているだけです。これには細心の注意を払ってください。クエリの実行は、結果セットの列挙を開始したときにのみ延期されます。そしてこれが見解で起こるならば、これはパフォーマンスにとって破滅的であるかもしれません。 EFクエリを積極的に実行するには.ToList()最後に。 - Darin Dimitrov
  • @tugberkでは、Webサイト上で複数のユーザーを並行してシミュレートして、高負荷の下でどのように動作するかを確認するための負荷テストツールが必要になります。ミニプロファイラーはあなたのウェブサイトの負荷をシミュレートすることはできません。 ADO.NETクエリを確認して最適化し、多数のユーザーがヒットしたときに実際の状況でサイトがどのように動作するかを確認する必要がある場合には役に立ちません。 - Darin Dimitrov

43

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

非同期コントローラがある場合でも、プールからスレッドを取得しますが、リクエストを処理している間はスレッドを放棄し、何かが発生するのを待って(そしてそのスレッドを別のリクエストに渡すことができます)再度それはプールから1つを得ます。

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

Aほぼ実生活の例... それはバスに乗るのと同じように考えてください。乗車を待っている人が5人いて、最初に乗車して料金を支払って座る(運転手が要求に応えて)、乗車(運転手が要求に応えている)あなたのお金を見つけなさい。あなたがあなたのポケットにぶつかると、運転手はあなたをあきらめて、次の二人を乗せます(彼らの要求に応えます)、あなたがあなたのお金を見つけると、ドライバは再びあなたと話し合い始めます(あなたの要求を完了する)あなたはやりましたが、3番目と4番目の人々はあなたが奉仕を受けている途中で奉仕されました。これは、ドライバがプールからの唯一のスレッドで、乗客がリクエストであることを意味します。 2人のドライバーがいた場合どのように動作するかを書くのは複雑すぎましたが、想像できますが...

非同期コントローラがなければ、あなたの後ろの乗客はあなたがあなたのお金を探す間、長い間待たなければならないでしょう、その間バスの運転手は仕事をしていないでしょう。

つまり、多くの人が自分のお金がどこにあるのかわからない(つまり、ドライバが要求したことに応答するのに長い時間がかかる)場合、非同期コントローラが要求のスループットを向上させることができます。 aysncコントローラがなければ、誰もが目の前の人が完全に対処されるまで待ちます。ただし、MVCでは、1つのバスに多数のバスドライバがあることを忘れないでください。非同期は自動的な選択ではありません。


  • とてもいいアナロジー。ありがとうございました。 - Pittsburgh DBA
  • 説明が気に入りました。ありがとう - Omer Cansizoglu
  • それを説明するための優れた方法。ありがとう、 - Anand Vyas
  • Darinの回答と組み合わせたあなたの回答は、非同期コントローラの背後にあるメカニズム全体、それが何であるか、そしてもっと重要なことはそうではないものをまとめたものです。 - Nirman
  • これは良いアナロジーですが、私の唯一の質問はこれです。あなたのアナロジーの中で彼のポケットにこもっている人は、私たちのアプリケーションではある種の作業/処理になるでしょう。本当にそれは別のスレッドですか?では、ここで何を得ますか - Tomuke

9

ここには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.StartNewcallは.NETスレッドプールで操作をキューに入れます。スレッドプールスレッドの性質は、再利用することです(多数のスレッドを作成または破棄するコストを回避するため)。操作が完了すると、スレッドはプールに解放されて別の要求で再利用されます(操作でオブジェクトを作成しない限り、Garbage Collectorは実際には関与しません。その場合は通常どおりに収集されます)。スコーピング)。

ASP.NETに関する限り、ここでは特別な操作はありません。 ASP.NET要求は、非同期タスクに関係なく完了します。唯一の懸念はあなたのスレッドプールが飽和しているかどうかであるかもしれません(すなわち、現在要求を処理するために利用可能なスレッドがなく、プールの設定はそれ以上のスレッドを作成することを許可しません)。タスクの開始を待っていますプールスレッドが利用可能になるまで。


  • ありがとうございます。私はあなたの答えを読んだ後、私はコードサンプルで質問を編集しました。見てもらえますか? - tugberk
  • 私のアップデートがあなたの追加質問に答えてくれることを願っています。 - Paul Turner
  • あなたは私のためにそこに魔法の文を持っています:Task.Factory.StartNewcallは.NETスレッドプールで操作をキューに入れます。。この文脈では、どちらが正しいですか。1 - )新しいスレッドを作成し、それが完了すると、そのスレッドはthreadpoolに戻り、そこで再び使用されるのを待ちます。2-)スレッドプールからスレッドを取得し、そのスレッドはスレッドプールに戻り、そこで再び使用されるのを待ちます。3-)それは最も効率的なアプローチを取り、それらのどちらかをすることができます。 - tugberk
  • スレッドプールは、必要に応じてスレッドを作成し、使用されていない場合はスレッドをリサイクルします。正確な動作はCLRのバージョンによって異なります。あなたはそれに関する特定の情報をここで見つけることができますmsdn.microsoft.com/ja-jp/library/0ka9477y.aspx - Paul Turner
  • スレッドプールはCLR内で独自のものです。 「認識」しているその他のコンポーネントプールのスレッドは、スレッドプールスレッドを作成して破棄するのではなく、スレッドプールスレッド(適切な場合)を使用することを示しています。スレッドの作成または破棄は比較的高価な操作なので、プールを使用することは短期間の操作で大きな効率の向上をもたらします。 - Paul Turner

2

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

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

一方で、非同期コードに関してあなたが聞いているコメント+提案は、一粒の塩でなされるべきです。初心者にとっては、何かを非同期にしただけでは必ずしもスケーラビリティが向上するわけではなく、場合によってはアプリケーションのスケーラビリティが低下することもあります。あなたが "大量のトラフィック..."について投稿した他のコメントも、特定の文脈においてのみ正しいです。それは本当にあなたの操作が何をしているか、そしてそれらがシステムの他の部分とどのようにやり取りするかにかかっています。

つまり、多くの人が非同期について多くの意見を持っていますが、文脈の外では正しくないかもしれません。私はあなたの正確な問題に焦点を当てて、非同期コントローラなどが実際にどのように扱っているかを見るために基本的な性能テストをしたいと思います。きみの応用。


  • 私はその文書を何百回も読んだことがありますが、それでも私は非常に混乱しています(おそらく問題は私が知っている人です)。あなたが見回すと、あなたは私の質問で見ることができるようにあなたはASP.NET MVC上の非同期性について非常に矛盾したコメントを見る。 - tugberk
  • 最後の文では、コントローラーのアクションの中で、私はデータベースに別々に5回問い合わせをしていました(私がしなければなりませんでした)、それはすべて約400ミリ秒かかりました。その後、AsyncControllerを実装し、それらを並行して実行します。応答時間は劇的に約に短縮されました。 200ミリ秒しかし、スレッドがいくつ作成されるのか、それらが終了した後にそれらのスレッドに何が起こるのかわかりません。GC私のアプリがメモリリークを起こさないようにするために、完了したらすぐにそれらを削除してきれいにします。その部分についての任意のアイデア。 - tugberk
  • デバッガを接続して見つけてください。 - A.R.
  • msdnリンクが壊れています - Travis J

0

まず最初はMVCではなく、スレッドプールを管理するIISです。そのため、MVCまたはASP.NETアプリケーションに送信される要求はすべて、スレッドプールで管理されているスレッドから処理されます。アプリAsynchを作成した場合にのみ、彼は別のスレッドでこのアクションを呼び出し、他の要求を処理できるようにすぐにスレッドを解放します。

私は詳細ビデオで同じことを説明しました(http://www.youtube.com/watch?v=wvg13n5V0V0/「MVC Asynchコントローラとスレッド枯渇」は、MVCでスレッド枯渇がどのように発生し、MVC Asynchコントローラを使用して最小化されるかを示します。MVCasynchとMVCの要求キューがどのように減少するかを確認できます。同期操作にとって最悪の状況です。

リンクされた質問


関連する質問

最近の質問