ASP.NET MVC 4アプリケーションでの非同期プログラミング

MVCの学習に関する記事を読んでRouRの コメントを見て、このトピックに非常に興味を持ち、外出中に彼が示した元の記事を翻訳することにしました。



親愛なるハラジテルの場合、それは面白いです-カットをお願いします!



awaitキーワードを使用した非同期プログラミングの観点から、C#5.0の新機能を紹介します。 特にASP.NET MVC 4 Webアプリケーションの場合。」





私はしばらくの間Visual Studio Async CTPいじくり回しましたが、今ではそれがどのように機能し、賢く使用するかについて十分な考えを持っています。 そして、それは私がそれについて書くことができることを意味します。



ほとんどのソフトウェアの主な問題は、長い実行時間を必要とする操作です。 ユーザーはそのような操作を開始します-そしてそれは完了するまでプログラムのメインスレッドをブロックします。 その結果、不便なソフトウェアユーザーと不満のある顧客を獲得しています。 私たち一人一人がそのようなプログラムに出会ったと確信しています!



非同期プログラミングは、新しいパラダイムとはほど遠いもので、.NET 1.0の時代にはすでにありました。 私は3年前にプログラミングにあまり興味がなかったので、この概念は今日ではまったく新しいものです。 私が読んでいる限り、この概念は非同期プログラミングの.NETの観点から大きく進化しており、私には思えるように、今では最高の段階にあります。



一方で、非同期プログラミングで混乱するのは非常に簡単です。既に何度か起こったことです。 私は行ったり来たりして、このことがどのように機能し、どこで使用するかについて多くのことを考えました。 そして、最終的には正しく理解したと信じたい。 一方、非同期プログラミングでは、Webアプリケーションで使用すると混乱しやすくなります。作業の結果をすぐに出力できるUIストリームがないためです。 非同期かどうかにかかわらず、ユーザーはしばらく待つ必要があります。 これが、非同期プログラミングがWebアプリケーションにとって望ましくない理由です。 そして、あなたもそう思うなら(そして私自身もそう思っていた)、あなたはまだ何も理解していない!



操作が同期的で時間がかかる場合、メインスレッドを完了するまでブロックする以外に選択肢はありません。 また、同じ操作を非同期で行う場合、操作を開始して何かを続行し、操作が既に完了している場合にのみ操作の結果に戻ることを意味します。 操作の開始から終了までの間、スレッドは自由であり、他のことを行うことができます。 ほとんどの人はここで混乱すると思います。 追加のスレッドを作成することはオーバーヘッドの問題であり、問​​題につながる可能性がありますが、これはすでに過去にあるように思えます。 .NET 4.0では、スレッド数の制限が大幅に増加しましたが、これはどこでも多くのスレッドを作成する必要があるという意味ではありません-少なくとも今は次のスレッドを作成するときに心配する必要はありません。 非同期プログラミングに関する多くの論争があります:





これらすべての質問に対する正確な答えはわかりませんが、ここにいくつかの例を示します。





そうは言っても、それは何らかの形で非同期性を活用するときです。 それで問題は何ですか? そして問題は、この非同期モデルがどのように機能するかです。 Anders Halesbergが言うように、このソフトウェアモデルはコードを裏返しにしています。 例外処理を伴う非同期操作のネストされた呼び出しを行うのが非常に難しいことは言うまでもありません。



Visual Studio Async CTPとその仕組み



C#5.0では、同期に非常によく似た新しい非同期プログラミングモデルが作成されます。 それまでの間、開発者は現在のバージョンのCTPをリリースしており、Go-Liveライセンスで利用できます。 AsyncCTP仕様からの引用は次のとおりです。



" 非同期関数は、非同期呼び出しの記述を容易にするC#の新機能です。関数がawaitキーワードを使用し式に到達し、実行を開始すると、関数の残りの部分が操作の自動継続として再構築され、非同期関数から即座に戻ります。関数自体は引き続き実行されます。つまり、プログラマーではなく言語自体がコード実行の順序を構築するタスクを引き受けます。 クロノ・コードは、論理的な構造を獲得します。」



非同期関数は、 非同期修飾子でマークされたクラスメソッドまたは匿名関数です。 Taskクラスのオブジェクトと、任意のタイプTの Task <T>の両方を返すことができます。そのような関数では、 待機することができます。 asyncとマークされた関数がvoid (これも有効)を返す場合、 awaitは使用できません。 しかし、他方では、 Tとして任意の型を使用できます



ASP.NET MVC 4およびC#非同期機能



ASP.NET MVCアプリケーションで非同期を適切に使用すると、パフォーマンスに非常に良い影響を与える可能性があります。 信じられないかもしれませんが、そうです。 次に、その方法を示します。



それでは、なぜ私たちはどこでもなく、すべてを非同期に行うのですか? そして、それは非常に難しく、エラーに満ちており、その後アプリケーションを維持するのが最終的に難しいからです。 しかし、新しい非同期モデルでは、これはすぐに変わります。



ASP.NET MVC 4では、非同期プログラミングが大きく変わりました。 ASP.NET MVC 3では、コントローラーをAsyncControllerから継承し、特定のパターンに準拠する必要があることをご存知かもしれません。 これについては、記事「 ASP.NET MVCで非同期コントローラーを使用する参照してください。



ASP.NET MVC 4では、AsyncControllerを使用する必要がなくなりました。async キーワードでコントローラーをマークし、 TaskまたはTask <T>クラス( Tは通常ActionResult )のオブジェクトを返すだけです。



1つのアプリケーションで2つの例を同時に組み合わせてみて、同じアクション(同期的および非同期的)をそれぞれ実行しようとしました。 私もストレステストを行いましたが、その結果は衝撃的でした! それでは、コードを見てみましょう。



まず、新しいASP.NET Web APIを使用して最も単純なRESTサービスを作成しました。 コレクションをメモリに保存する最も単純なモデル(代わりにデータベースを使用できます):

public class Car { public string Make; public string Model; public int Year; public int Doors; public string Colour; public float Price; public int Mileage; } public class CarService { public List<Car> GetCars() { List<Car> Cars = new List<Car> { new Car { Make="Audi", Model="A4", Year=1995, Doors=4, Colour="Red", Price=2995f, Mileage=122458 }, new Car { Make="Ford", Model="Focus", Year=2002, Doors=5, Colour="Black", Price=3250f, Mileage=68500 }, new Car { Make="BMW", Model="5 Series", Year=2006, Doors=4, Colour="Grey", Price=24950f, Mileage=19500 } //This keeps going like that }; return Cars; } }
      
      





そしてサービスコード:

 public class CarsController : ApiController { public IEnumerable<Car> Get() { var service = new CarService(); return service.GetCars(); } }
      
      





だから、私にはサービスがあります。 次に、サービスからデータを受信して​​表示するWebアプリケーションを作成します。 これを行うために、データを受信して​​データをデシリアライズするサービスクラスを作成しました。非同期呼び出しには新しいHttpClientを使用し、同期呼び出しには通常のWebClientを使用しました 。 私もJson.NETを使用しました

 public class CarRESTService { readonly string uri = "http://localhost:2236/api/cars"; public List<Car> GetCars() { using (WebClient webClient = new WebClient()) { return JsonConvert.DeserializeObject<List<Car>> ( webClient.DownloadString(uri) ); } } public async Task<List<Car>> GetCarsAsync() { using (HttpClient httpClient = new HttpClient()) { return JsonConvert.DeserializeObject<List<Car>> ( await httpClient.GetStringAsync(uri) ); } } }
      
      





GetCarsメソッドについて言うことは何もありません-それは簡単です。 しかし、 GetCarsAsyncについては、次のことが言えます。





最後に、コントローラーを示します。

 public class HomeController : Controller { private CarRESTService service = new CarRESTService(); public async Task<ActionResult> Index() { return View("index", await service.GetCarsAsync()); } public ActionResult IndexSync() { return View("index", service.GetCars()); } }
      
      







そこで、 タスク<ActionResult>を返す非同期関数であるIndexと、通常の同期呼び出しであるIndexSyncを取得しました 。 アドレス/ home / indexまたは/ home / indexsyncにアクセスすると 、ページを開く速度に大きな違いは見られません-それはほぼ同じです。



これらのメソッドの動作の違いを測定するために、MS Visual Studio 2010 Ultimateで負荷テストを作成しました。 これらの2つのページを2分以内に開きます。50ユーザーの負荷で開始し、5秒ごとに20ユーザーずつ徐々に増やしていきます。 最大制限は500ユーザーです。 結果を見る(クリック可能):



画像



同期バージョンの場合、応答時間は11.2秒、非同期バージョンの場合は3.65秒であることがわかります。 その違いは明らかだと思います。



その瞬間から、私は「40ms(ネットワーク、ファイルシステム、つまりすべてのメインI / Oに関連する操作)よりも長い時間がかかるすべて-これはすべて非同期でなければならない!」という概念の支持者になりました




All Articles