0

ASP.NET アプリケーションのパフォーマンスを向上させる方法を試しています。私が検討していることの 1 つは、並列処理を使用し、操作を非同期にして、処理時間を短縮し、スループットを向上させることです。ページをレンダリングするために複数のデータベース取得を発行するという、私たちがかなり頻繁に行っていることをモックアップすることから始めました。

public ActionResult Index()
{
    var dal = new Dal();
    var cases = new List<Case>();
    cases.AddRange( dal.GetAssignedCases() );
    cases.AddRange( dal.GetNewCases() );
    return View( "Cases", cases );
}

2 つの Dal メソッドを使用Thread.Sleep(2000)してクエリをシミュレートし、ハードコードされたオブジェクトのコレクションを返します。これを Apache Bench を使用して実行するab -c 1 -n 1と、約 4 秒かかります。それを改善しようとする私の最初の試みは:

public ActionResult Index()
{
    var dal = new Dal();
    var assignedCases = Task.Factory.StartNew( () => dal.GetAssignedCases() );
    var newCases = Task.Factory.StartNew( () => dal.GetNewCases() );
    IEnumerable<Case>[] allCases = Task.WhenAll( assignedCases, newCases ).Result;
    return View( "Cases", allCases.SelectMany( c => c ) );
}

同じabコマンドを使用してこれを実行すると、約 2 秒と表示されます。これは、それぞれ 2 秒かかる 2 つのタスクを実行しているが、それらが並行して実行されているためです。

ベンチマークを 10 個の同時リクエスト (つまりab -n 10 -c 10) に変更すると、次のようになります。

Fulfilled  Original Parallel
 50%         4014     2038
 66%         4015     2039
 75%         4017     4011

100% までの残りの数値は、両方の列で類似しています。

ここで実行しているのは、スレッド プールの競合であると想定しています。リクエストの約 3 分の 2 はすぐに処理され、その後はスレッドがリクエストを処理するのを待っています。したがって、ミックスに非同期を追加すると、さらに多くのリクエストをより迅速に処理できるようになると思います。そこから問題が発生し始めますが、問題が長時間実行されるクエリをシミュレートする方法なのか、言語機能の使用方法なのか、それとも完全に間違った方向に進んでいるだけなのかわかりません。トンネルの先には対向列車が。:-)

最初にしたことは、DalAsync を作成することでした。DalAsync では、 を に置き換え、各メソッドをキーワードでマークThread.Sleep(2000)し、戻り値の型を からに変更しました。次に、6 件のブログ投稿と MSDN 記事で読んだ情報を組み合わせて、新しいコントローラー メソッドを作成しました。await Task.Delay(2000)asyncIEnumerable<Case>Task<IEnumerable<Case>>

public async Task<ActionResult> Index()
{
    var dal = new DalAsync();
    var assignedCases = dal.GetAssignedCasesAsync();
    var newCases = dal.GetNewCasesAsync();
    var allCases = await Task.WhenAll( assignedCases, newCases );
    return View( "Cases", allCases.SelectMany( c => c ) );
}

これを使用して実行するabと、1回のリクエストでもタイムアウトしてしまいます。また、次のバリエーションも試しました。これは機能しますが、元のバージョンとほぼ同じ数値を返します (クエリを再度シリアル化しているように見えるため、これは理にかなっています)。

var assignedCases = await dal.GetAssignedCasesAsync();
var newCases = await dal.GetNewCasesAsync();
var allCases = new List<Case>( assignedCases );
allCases.AddRange( newCases );

私がしたいことは次のとおりです。

  • 2 つのクエリを並行して実行する
  • コントローラーが Dal メソッドの応答を待機している場合、コントローラーはスレッドを解放し、他の要求を実行できるようにします。
4

2 に答える 2

0

最初のコード サンプルは機能するはずですが、私の目には少し奇妙に見えます。Task.WhenAll非ブロッキング操作として導入されました。つまり、 を使用しますawait Task.WhenAll(myTasks)。を使用する.Resultと、ブロック操作になりますが、このように使用するのはあまり自然ではありません。

あなたが本当にTask.WaitAll(params Task[])求めているのは、ブロッキング操作になるように設計されていることだと思います。

ただし、2 番目のコード サンプルはほぼ完璧で、まさに私が求めているものです。コードベース全体に非同期コードを実装すると、常によりクリーンな実装になります。

于 2013-12-19T19:33:32.643 に答える