W poprzednim wpisie zająłem się wpływem sesji na wydajność kontrolerów. Dzisiaj zajmiemy się asynchroniczny kontrolerami, które znaczącą zostały uproszczone w .NET 4.5.
Żeby zrozumieć jak działają asynchroniczne kontrolery, należy zdawać sobie sprawę, jak działa przetwarzanie zapytań. Załóżmy, że wysłanych jest 1000 zapytać do serwera. Czy zostaną one obsłużone jednocześnie, a może sekwencyjnie, jedno po jednym? ASP.NET MVC ma pulę wątków, przeznaczoną do przetwarzania zapytań. Jeśli zatem pula ma pojemność 5, wtedy wyłącznie 5 zapytań może zostać obsłużonych jednocześnie – reszta musi poczekać na swoją kolej.
Szczegóły te może nas nie interesują, ale co w przypadku gdy któryś z kontrolerów czeka na jakieś zasoby? Łatwo sobie wyobrazić scenariusz, w którym akcja łączy się z inną usługą w celu pobrania danych. Jeśli połączenie się z usługą zajmuje 5 sekund, wtedy wątek jest blokowany przez cały ten czas – uniemożliwiając przetworzenie innych zapytań, które być może nie muszą czekać na żadne inne zasoby.
Szkoda więc marnować cenny wątek z puli, tylko po to, aby czekał na jakieś zasoby. Z pomocą przychodzą kontrolery asynchroniczne, które zostały znacząco uproszczone w .NET 4..5. Operacje IO, w których trzeba czekać długo na zasoby, umieszcza się po prostu w osobnym wątku. Ponadto kontroler powinien dziedziczyć po AsynContorler. Stwórzmy zatem dwa kontrolery, jeden synchroniczny, a drugi asynchroniczny. Oba będą wykonywać operacje, które po prostu czekają na jakieś zasoby:
public class AsynchronousController : AsyncController { // // GET: /Async/ public async Task<ActionResult> Index() { int value = await Task.Factory.StartNew<int>(WaitingForResponse); return Content(value.ToString(CultureInfo.InvariantCulture)); } private int WaitingForResponse() { Thread.Sleep(1000); return 5; } } public class SyncController : Controller { // // GET: /Sync/ public ActionResult Index() { int value = WaitingForResponse(); return Content(value.ToString(CultureInfo.InvariantCulture)); } private int WaitingForResponse() { Thread.Sleep(1000); return 5; } }
Proszę zwrócić uwagę, że koniecznie należy dziedziczyć po AsyncController .
Wiele osób nie rozumie tak naprawdę asynchronicznych kontrolerów i umieszcza w nich logikę, która jest po prostu czasochłonna. Nie przyniesie to żadnej optymalizacji. Asynchroniczne kontrolery są dla kodu, który musi czekać na jakieś zasoby a nie dla logiki, która konsumuje wiele cykli CPU. Jeśli mamy jakaś operację konsumującą CPU, wtedy tworzenie własnych wątków nie przyśpieszy wykonania a jedynie spowolni wątki ASP.NET MVC. Programowanie asynchroniczne nie jest tym samym co programowanie współbieżne.
Czym to rozwiązanie różni się od metod akcji opartych na await i async a ich kontroler nie dziedziczy po AsyncController? Przykład zawarty jest np. w pustym projekcie MVC 5 który zawiera autoryzacje.
AsyncController nie jest potrzebny od MVC 4
A możesz powiedzieć czemu ? Co zostało dodane ? Czy może zwykły kontroler został rozszerzony o bajery z AsyncController.
Fajny artykuł, chciałbym poprosić o jeszcze więcej przykładów i wytłumaczenia po co to jest.