¿Cuándo debería usar Async Controllers en ASP.NET MVC?

Me preocupa el uso de acciones asíncronas en ASP.NET MVC. ¿Cuándo mejora el rendimiento de mis aplicaciones y cuándo no ?

  1. ¿Es bueno usar acción asíncrona en cualquier lugar de ASP.NET MVC?
  2. Con respecto a los métodos disponibles: ¿debo usar palabras clave async / await cuando quiero consultar una base de datos (a través de EF / NHibernate / otro ORM)?
  3. ¿Cuántas veces puedo usar palabras clave de espera para consultar la base de datos de forma asincrónica en un solo método de acción?

Los métodos de acción asincrónica son útiles cuando una acción debe realizar varias operaciones independientes de larga ejecución.

Un uso típico de la clase AsyncController son las llamadas al servicio web de larga ejecución.

¿Deberían mis llamadas a la base de datos ser asincrónicas?

El grupo de subprocesos de IIS a menudo puede manejar muchas más solicitudes de locking simultáneas que un servidor de base de datos. Si la base de datos es el cuello de botella, las llamadas asincrónicas no acelerarán la respuesta de la base de datos. Sin un mecanismo de aceleración, el envío eficiente de más trabajo a un servidor de base de datos abrumado mediante el uso de llamadas asíncronas simplemente traslada la mayor parte de la carga a la base de datos. Si su DB es el cuello de botella, las llamadas asincrónicas no serán la bala mágica.

Deberías echarle un vistazo a 1 y 2 referencias

Derivado de los comentarios de @PanagiotisKanavos:

Además, asincrónico no significa paralelo. La ejecución asincrónica libera una valiosa cadena de subprocesos del locking para un recurso externo, sin complejidad o costo de rendimiento. Esto significa que la misma máquina IIS puede manejar más solicitudes concurrentes, no que se ejecutará más rápido.

También debe considerar que las llamadas de locking comienzan con un spinwait intensivo de CPU. Durante los momentos de estrés, las llamadas de locking darán lugar a demoras crecientes y al reciclaje del grupo de aplicaciones. Las llamadas asincrónicas simplemente evitan esto

Puede encontrar útil mi artículo de MSDN sobre el tema ; Tomé mucho espacio en ese artículo que describe cuándo debe usar async en ASP.NET, no solo cómo usar async en ASP.NET.

Me preocupa el uso de acciones asíncronas en ASP.NET MVC. Cuando mejora el rendimiento de mis aplicaciones, y cuándo, no.

Primero, entiende que async / await tiene que ver con liberar hilos . En aplicaciones de GUI, se trata principalmente de liberar el hilo de la GUI para que la experiencia del usuario sea mejor. En las aplicaciones de servidor (incluido ASP.NET MVC), se trata principalmente de liberar el hilo de solicitud para que el servidor pueda escalar.

En particular, no:

  • Haga que sus solicitudes individuales se completen más rápido. De hecho, completarán (solo un poquito) más lento.
  • Regrese a la persona que llama / al navegador cuando lo await . await solo “cede” al grupo de subprocesos ASP.NET, no al navegador.

La primera pregunta es: ¿es bueno usar acción asíncrona en cualquier lugar de ASP.NET MVC?

Diría que es bueno usarlo en todos los lugares donde estés haciendo I / O. Sin embargo, puede que no sea necesariamente beneficioso (ver más abajo).

Sin embargo, es malo usarlo para los métodos vinculados a la CPU. A veces los desarrolladores creen que pueden obtener los beneficios de la Task.Run en sus controladores, y esta es una idea horrible. Debido a que el código termina liberando el hilo de solicitud al tomar otro hilo, entonces no hay ningún beneficio en absoluto (¡y de hecho, están tomando la penalidad de los conmutadores de hilo adicionales)!

¿Debo usar palabras clave async / await cuando quiero consultar la base de datos (a través de EF / NHibernate / otro ORM)?

Puedes usar los métodos disponibles que tengas a mano. En este momento, la mayoría de los principales jugadores admiten async , pero hay algunos que no lo hacen. Si su ORM no es compatible con la async , no intente Task.Run en Task.Run ni nada de eso (consulte más arriba).

Tenga en cuenta que dije ” podría usar”. Si está hablando de ASP.NET MVC con un único back-end de base de datos, entonces (casi con certeza) no obtendrá ningún beneficio de escalabilidad de async . Esto se debe a que IIS puede manejar muchas más solicitudes simultáneas que una sola instancia de SQL Server (u otro RDBMS clásico). Sin embargo, si su back-end es más moderno -un clúster de SQL Server, Azure SQL, NoSQL, etc.- y su back-end puede escalar, y su cuello de botella de escalabilidad es IIS, entonces puede obtener un beneficio de escalabilidad de async .

Tercera pregunta: ¿Cuántas veces puedo usar aguardar palabras clave para consultar la base de datos de forma asincrónica en UN único método de acción?

Tantos como quieras. Sin embargo, tenga en cuenta que muchos ORM tienen una regla de una operación por conexión. En particular, EF solo permite una sola operación por DbContext; esto es cierto si la operación es síncrona o asíncrona.

Además, tenga en cuenta la escalabilidad de su backend de nuevo. Si está accediendo a una instancia única de SQL Server, y su IIS ya es capaz de mantener SQLServer a su capacidad máxima, duplicar o triplicar la presión sobre SQLServer no lo ayudará en absoluto.

¿Es bueno usar acción asíncrona en cualquier lugar de ASP.NET MVC?

Como es habitual en la progtwigción, depende . Siempre hay una compensación cuando se va por un camino determinado.

async-await brilla en lugares donde sabe que recibirá solicitudes concurrentes a su servicio y desea poder escalar bien. ¿Cómo funciona la async-await ¿ async-await ayuda para escalar? En el hecho de que al invocar una llamada asincrónica IO de forma síncrona , como una llamada de red o accediendo a su base de datos, el subproceso actual que es responsable de la ejecución se bloquea a la espera de que finalice la solicitud. Cuando utiliza async-await , habilita el marco para crear una máquina de estado para usted que se asegura de que después de que se complete la llamada IO, su método continúe ejecutándose desde donde se quedó.

Una cosa a tener en cuenta es que esta máquina de estado tiene una sobrecarga sutil. Hacer que un método sea asincrónico no lo hace ejecutar más rápido , y ese es un factor importante para comprender y una idea errónea que muchas personas tienen.

Otra cosa a tener en cuenta cuando usas async-await es el hecho de que es asíncrono en todo su sentido , lo que significa que verás la asincronía penetrar toda la stack de llamadas, de arriba a abajo. Esto significa que si desea exponer las API síncronas, a menudo se encontrará duplicando una cierta cantidad de código, ya que la sincronización y la sincronización no se combinan muy bien.

¿Debo usar palabras clave async / await cuando quiero consultar la base de datos (a través de EF / NHibernate / otro ORM)?

Si elige ir por el camino del uso de llamadas asincrónicas IO, entonces sí, async-await será una buena opción, ya que cada vez más proveedores de bases de datos modernos exponen el método asincrónico implementando el TAP (Patrón asincrónico de tareas).

¿Cuántas veces puedo usar aguardar palabras clave para consultar la base de datos de forma asincrónica en UN único método de acción?

Todos los que desee, siempre y cuando siga las reglas establecidas por su proveedor de base de datos. No hay límite para la cantidad de llamadas asíncronas que puede realizar. Si tiene consultas que son independientes entre sí y se pueden realizar de forma simultánea, puede girar una nueva tarea para cada una y await Task.WhenAll espera a que ambas se completen.

async acciones async ayudan mejor cuando las acciones realizan algunas operaciones de I / O a DB o algunas llamadas enlazadas a la red donde el hilo que procesa la solicitud se detendrá antes de recibir respuesta de la DB o llamada enlazada a la red que acaba de invocar. Lo mejor es que lo use aguarde con ellos y realmente mejorará la capacidad de respuesta de su aplicación (porque menos subprocesos de entrada / salida ASP se estancarán mientras espera la DB o cualquier otra operación como esa). En todas mis aplicaciones cada vez que necesité muchas llamadas a DB, siempre las envolví en un método deseable y las llamé con la palabra clave await.

Mis 5 centavos:

  1. Use async/await si y solo si realiza una operación IO, como DB o un servicio web de servicio externo.
  2. Siempre prefiere las llamadas asíncronas a DB.
  3. Cada vez que consulta la base de datos.

PD: hay casos excepcionales para el punto 1, pero necesita tener una buena comprensión de las partes internas asincrónicas para esto.

Como ventaja adicional, puede hacer pocas llamadas IO en paralelo si es necesario:

 Task task1 = FooAsync(); // launch it, but don't wait for result Task task2 = BarAsync(); // launch bar; now both foo and bar are running await Task.WhenAll(task1, task2); // this is better in regard to exception handling // use task1.Result, task2.Result 

Como sabe, MVC admite controladores asíncronos y debe aprovecharlo. En caso de que su Controlador realice una operación larga (puede ser una E / S basada en disco o una llamada de red a otro servicio remoto), si la solicitud se maneja de manera síncrona, el hilo IIS estará ocupado todo el tiempo. Como resultado, el hilo solo está esperando a que se complete la operación larga. Se puede utilizar mejor atendiendo otras solicitudes mientras la operación solicitada en primer lugar está en progreso. Esto ayudará a atender más solicitudes simultáneas. Su servicio web será altamente escalable y no se encontrará fácilmente con el problema C10k . Es una buena idea usar async / await para consultas db. y sí, puede usarlos tantas veces como lo considere apropiado.

Eche un vistazo aquí para obtener excelentes consejos.