Asynchroniczne kontrolery

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.

4 thoughts on “Asynchroniczne kontrolery”

  1. 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.

  2. A możesz powiedzieć czemu ? Co zostało dodane ? Czy może zwykły kontroler został rozszerzony o bajery z AsyncController.

  3. Fajny artykuł, chciałbym poprosić o jeszcze więcej przykładów i wytłumaczenia po co to jest.

Leave a Reply

Your email address will not be published.