Las operaciones asincrónicas en ASP.NET MVC usan un hilo de ThreadPool en .NET 4

158

Después de esta pregunta, me hace sentir cómodo al usar operaciones asíncronas en ASP.NET MVC. Entonces, escribí dos publicaciones de blog sobre eso:

Tengo demasiados malentendidos en mi mente sobre las operaciones asincrónicas en ASP.NET MVC.

Siempre escucho esta oración: la aplicación puede escalar mejor si las operaciones se ejecutan de forma asincrónica

Y también escuché mucho este tipo de oraciones: si tiene un gran volumen de tráfico, es mejor que no realice sus consultas de forma asincrónica, ya que consumir 2 hilos adicionales para atender una solicitud le quita recursos a otras solicitudes entrantes.

Creo que esas dos oraciones son inconsistentes.

No tengo mucha información sobre cómo funciona threadpool en ASP.NET, pero sé que threadpool tiene un tamaño limitado para hilos. Entonces, la segunda oración tiene que estar relacionada con este tema.

¿Y me gustaría saber si las operaciones asincrónicas en ASP.NET MVC usan un hilo de ThreadPool en .NET 4?

Por ejemplo, cuando implementamos un AsyncController, ¿cómo se estructura la aplicación? Si obtengo mucho tráfico, ¿es una buena idea implementar AsyncController?

¿Hay alguien por ahí que pueda quitarme esta cortina negra frente a mis ojos y explicarme el trato sobre la asincronía en ASP.NET MVC 3 (NET 4)?

Editar:

He leído este documento a continuación casi cientos de veces y entiendo el trato principal, pero todavía tengo confusión porque hay demasiados comentarios inconsistentes.

Uso de un controlador asíncrono en ASP.NET MVC

Editar:

Supongamos que tengo una acción de controlador como la siguiente (no es una implementación de AsyncController):

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

Como puede ver aquí, disparo una operación y me olvido de eso. Luego, regreso inmediatamente sin esperar a que se complete.

En este caso, ¿esto tiene que usar un hilo de threadpool? Si es así, después de que se complete, ¿qué pasa con ese hilo? ¿ GCEntra y limpia justo después de que se complete?

Editar:

Para la respuesta de @ Darin, aquí hay una muestra de código asíncrono que habla con la base de datos:

public class FooController : AsyncController {

    //EF 4.2 DbContext instance
    MyContext _context = new MyContext();

    public void IndexAsync() { 

        AsyncManager.OutstandingOperations.Increment(3);

        Task<IEnumerable<Foo>>.Factory.StartNew(() => { 

           return 
                _context.Foos;
        }).ContinueWith(t => {

            AsyncManager.Parameters["foos"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        Task<IEnumerable<Bars>>.Factory.StartNew(() => { 

           return 
                _context.Bars;
        }).ContinueWith(t => {

            AsyncManager.Parameters["bars"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        Task<IEnumerable<FooBar>>.Factory.StartNew(() => { 

           return 
                _context.FooBars;
        }).ContinueWith(t => {

            AsyncManager.Parameters["foobars"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });
    }

    public ViewResult IndexCompleted(
        IEnumerable<Foo> foos, 
        IEnumerable<Bar> bars,
        IEnumerable<FooBar> foobars) {

        //Do the regular stuff and return

    }
}
tugberk
fuente
No estoy seguro de la respuesta, pero vale la pena señalar que los procesos multihilo y asincrónicos son cosas diferentes. Por lo tanto, sería posible tener un número fijo de subprocesos con manejo asincrónico. Lo que sucedería es que cuando una página tiene que bloquear por ejemplo, E / S, otra página tendría la oportunidad de ejecutarse en el mismo hilo. Es por eso que ambas afirmaciones pueden ser verdaderas, async puede acelerar las cosas, pero demasiados hilos es un problema.
Chris Chilvers
@ChrisChilvers Sí, el subprocesamiento múltiple no siempre es necesario en una operación asincrónica. Ya me di cuenta de eso, pero creo que no tengo control sobre eso, por lo que puedo decir. AsyncController gira cuántos hilos quiere desde mi punto de vista, pero tampoco estoy seguro de eso. ¿Existe también una noción de threadpool en aplicaciones de escritorio como WPF? Creo que el número de hilos no es un problema en ese tipo de aplicaciones.
tugberk
66
Echa un vistazo al video Threading with Jeff Richter
oleksii
Creo que el problema (y, por lo tanto, la inconsistencia) es que la segunda declaración usa asíncrona cuando significa muchos hilos. Esto podría deberse a que así es como asp.net ha implementado páginas asincrónicas y, por lo tanto, la implementación específica ha confundido el problema (ya que el nombre de la característica que causa el problema sería páginas asincrónicas), pero no estoy seguro acerca de la implementación específica . Entonces, o significan "muchos hilos", o significan "páginas asíncronas dentro de asp.net versión X", ya que las versiones futuras pueden cambiar la implementación. O simplemente se refieren al uso del grupo de subprocesos para realizar asíncrono dentro de una página.
Chris Chilvers
@ChrisChilvers oh, hombre! Estoy más confundido después de estos comentarios: s
tugberk

Respuestas:

177

Aquí hay un excelente artículo que le recomendaría leer para comprender mejor el procesamiento asincrónico en ASP.NET (que es lo que básicamente representan los controladores asincrónicos).

Consideremos primero una acción síncrona estándar:

public ActionResult Index()
{
    // some processing
    return View();
}

Cuando se realiza una solicitud para esta acción, se extrae un subproceso del grupo de subprocesos y el cuerpo de esta acción se ejecuta en este subproceso. Entonces, si el procesamiento dentro de esta acción es lento, está bloqueando este hilo para todo el procesamiento, por lo que este hilo no se puede reutilizar para procesar otras solicitudes. Al final de la ejecución de la solicitud, el subproceso se devuelve al grupo de subprocesos.

Ahora tomemos un ejemplo del patrón asincrónico:

public void IndexAsync()
{
    // perform some processing
}

public ActionResult IndexCompleted(object result)
{
    return View();
}

Cuando se envía una solicitud a la acción Índice, se extrae un subproceso del grupo de subprocesos y IndexAsyncse ejecuta el cuerpo del método. Una vez que el cuerpo de este método termina de ejecutarse, el subproceso se devuelve al grupo de subprocesos. Luego, utilizando el estándar AsyncManager.OutstandingOperations, una vez que señala la finalización de la operación asincrónica, se extrae otro subproceso del grupo de subprocesos y IndexCompletedse ejecuta el cuerpo de la acción y el resultado se entrega al cliente.

Entonces, lo que podemos ver en este patrón es que una sola solicitud HTTP de cliente podría ser ejecutada por dos hilos diferentes.

Ahora la parte interesante sucede dentro del IndexAsyncmétodo. Si tiene una operación de bloqueo dentro, está desperdiciando totalmente el propósito de los controladores asíncronos porque está bloqueando el subproceso de trabajo (recuerde que el cuerpo de esta acción se ejecuta en un subproceso extraído del grupo de subprocesos).

Entonces, ¿cuándo podemos aprovechar realmente los controladores asíncronos que podría preguntar?

En mi humilde opinión, podemos ganar más cuando tenemos operaciones intensivas de E / S (como llamadas de bases de datos y redes a servicios remotos). Si tiene una operación intensiva de CPU, las acciones asincrónicas no le brindarán muchos beneficios.

Entonces, ¿por qué podemos beneficiarnos de las operaciones intensivas de E / S? Porque podríamos utilizar los puertos de E / S de terminación . IOCP es extremadamente poderoso porque no consume ningún hilo o recurso en el servidor durante la ejecución de toda la operación.

¿Cómo trabajan?

Supongamos que queremos descargar el contenido de una página web remota utilizando el método WebClient.DownloadStringAsync . Llama a este método que registrará un IOCP dentro del sistema operativo y regresará de inmediato. Durante el procesamiento de toda la solicitud, no se consumen hilos en su servidor. Todo sucede en el servidor remoto. Esto podría llevar mucho tiempo, pero no le importa, ya que no está poniendo en peligro sus hilos de trabajo. Una vez que se recibe una respuesta, se señala el IOCP, se extrae un subproceso del grupo de subprocesos y se ejecuta la devolución de llamada en este subproceso. Pero como puede ver, durante todo el proceso, no hemos monopolizado ningún hilo.

Lo mismo ocurre con métodos como FileStream.BeginRead, SqlCommand.BeginExecute, ...

¿Qué pasa con la paralelización de múltiples llamadas a la base de datos? Suponga que tuvo una acción de controlador síncrono en la que realizó 4 llamadas de bloqueo de la base de datos en secuencia. Es fácil calcular que si cada llamada a la base de datos dura 200 ms, la acción de su controlador tardará aproximadamente 800 ms en ejecutarse.

Si no necesita ejecutar esas llamadas secuencialmente, ¿las paralelizaría mejoraría el rendimiento?

Esa es la gran pregunta, que no es fácil de responder. Tal vez sí tal vez no. Dependerá completamente de cómo implemente esas llamadas a la base de datos. Si usa controladores asíncronos y puertos de finalización de E / S como se discutió anteriormente, aumentará el rendimiento de esta acción del controlador y de otras acciones también, ya que no estará monopolizando los subprocesos de los trabajadores.

Por otro lado, si las implementa mal (con una llamada de base de datos de bloqueo realizada en un subproceso del grupo de subprocesos), básicamente reducirá el tiempo total de ejecución de esta acción a aproximadamente 200 ms, pero habría consumido 4 subprocesos de trabajo para que podría haber degradado el rendimiento de otras solicitudes que podrían pasar hambre debido a la falta de subprocesos en el grupo para procesarlas.

Por lo tanto, es muy difícil y si no se siente listo para realizar pruebas exhaustivas en su aplicación, no implemente controladores asíncronos, ya que es probable que haga más daño que beneficio. Impleméntelos solo si tiene una razón para hacerlo: por ejemplo, ha identificado que las acciones estándar del controlador síncrono son un cuello de botella para su aplicación (después de realizar extensas pruebas de carga y mediciones, por supuesto).

Ahora consideremos su ejemplo:

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

Cuando se recibe una solicitud para la acción Índice, se extrae un subproceso del grupo de subprocesos para ejecutar su cuerpo, pero su cuerpo solo programa una nueva tarea utilizando TPL . Por lo tanto, la ejecución de la acción finaliza y el subproceso se devuelve al grupo de subprocesos. Excepto que, TPL utiliza subprocesos del grupo de subprocesos para realizar su procesamiento. Entonces, incluso si el subproceso original se devolvió al grupo de subprocesos, ha extraído otro subproceso de este grupo para ejecutar el cuerpo de la tarea. Así que has puesto en peligro 2 hilos de tu preciosa piscina.

Ahora consideremos lo siguiente:

public ViewResult Index() { 

    new Thread(() => { 
        //Do an advanced looging here which takes a while
    }).Start();

    return View();
}

En este caso, estamos generando manualmente un hilo. En este caso, la ejecución del cuerpo de la acción de índice puede tardar un poco más (porque generar un nuevo hilo es más costoso que dibujar uno de un grupo existente). Pero la ejecución de la operación de registro avanzada se realizará en un subproceso que no forma parte del grupo. Por lo tanto, no estamos poniendo en peligro los subprocesos del grupo que permanecen libres para atender otras solicitudes.

Darin Dimitrov
fuente
1
Muy detallado, gracias! Supongamos que tenemos 4 tareas asíncronas ( System.Threading.Task) ejecutándose dentro del IndexAsyncmétodo. Dentro de esas operaciones, estamos haciendo llamadas db a un servidor. Entonces, todos ellos son operaciones intensivas de E / S, ¿verdad? En ese caso, ¿creamos 4 hilos separados (u obtenemos 4 hilos separados del grupo de hilos)? Suponiendo que tengo una máquina multinúcleo, también se ejecutarán en paralelo, ¿verdad?
tugberk
10
@tugberk, las llamadas a la base de datos son operaciones de E / S, pero todo dependerá de cómo las implemente. Si usa una llamada de bloqueo de la base de datos, como SqlCommand.ExecuteReadersi estuviera desperdiciando todo, ya que esta es una llamada de bloqueo. Está bloqueando el subproceso en el que se ejecuta esta llamada y si este subproceso es un subproceso del grupo, es muy malo. Usted se beneficiará sólo si se utiliza I / O Puertos completado: SqlCommand.BeginExecuteReader. Si no usa IOCP sin importar lo que haga, no use controladores asíncronos ya que causará más daño que beneficio en el rendimiento general de su aplicación.
Darin Dimitrov
1
Bueno, la mayoría de las veces uso el código EF primero. No estoy seguro si encaja. Pongo una muestra que muestra lo que generalmente hago. Actualicé la pregunta, ¿puedes echar un vistazo?
tugberk
3
@tugberk, los está ejecutando en paralelo, por lo que el tiempo total de ejecución es menor en comparación con si los ejecuta secuencialmente. Pero para ejecutarlos, utiliza hilos de trabajo. Bueno, en realidad EF es vago, así que cuando lo haces _context.Foo, en realidad no estás ejecutando nada. Solo estás construyendo un árbol de expresión. Ten mucho cuidado con esto. La ejecución de la consulta se aplaza solo cuando comienza a enumerar sobre el conjunto de resultados. Y si esto sucede en la vista, esto podría ser catastrófico para el rendimiento. Para ejecutar ansiosamente una consulta EF, agregue .ToList()al final.
Darin Dimitrov
2
@tugberk, necesitará una herramienta de prueba de carga para simular múltiples usuarios en paralelo en su sitio web para ver cómo se comporta bajo una carga pesada. Mini Profiler no puede simular la carga en su sitio web. Puede ayudarlo a ver y optimizar sus consultas de ADO.NET y presentar una solicitud única que es inútil cuando necesita ver cómo se comporta su sitio en una situación del mundo real cuando muchos usuarios lo están accediendo.
Darin Dimitrov
49

Sí, todos los hilos provienen del grupo de hilos. Su aplicación MVC ya es multiproceso, cuando se recibe una solicitud en un nuevo subproceso se tomará del grupo y se utilizará para atender la solicitud. Ese hilo estará 'bloqueado' (de otras solicitudes) hasta que la solicitud sea atendida y completada por completo. Si no hay ningún subproceso disponible en el grupo, la solicitud tendrá que esperar hasta que haya uno disponible.

Si tiene controladores asíncronos, aún obtienen un subproceso del grupo, pero mientras atienden la solicitud, pueden abandonar el subproceso, mientras esperan que ocurra algo (y ese subproceso se puede dar a otra solicitud) y cuando la solicitud original necesita un subproceso de nuevo se obtiene uno de la piscina.

La diferencia es que si tiene muchas solicitudes de larga duración (donde el subproceso está esperando una respuesta de algo), puede quedarse sin subprocesos del grupo para atender incluso las solicitudes básicas. Si tiene controladores asíncronos, no tiene más subprocesos, pero los subprocesos que están esperando se devuelven al grupo y pueden atender otras solicitudes.

Un ejemplo casi de la vida real ... Piense en ello como subir a un autobús, hay cinco personas esperando para subir, el primero sube, paga y se sienta (el conductor atendió su solicitud), usted sube (el conductor está reparando su solicitud) pero no puede encontrar su dinero; A medida que hurga en sus bolsillos, el conductor se da por vencido y recibe a las siguientes dos personas (atendiendo sus solicitudes), cuando encuentra su dinero, el conductor comienza a tratar con usted nuevamente (completando su solicitud): la quinta persona tiene que esperar hasta ya terminaste, pero la tercera y cuarta personas fueron atendidas mientras estabas a mitad de camino. Esto significa que el conductor es el único hilo del grupo y los pasajeros son las solicitudes. Era demasiado complicado escribir cómo funcionaría si hubiera dos controladores, pero te puedes imaginar ...

Sin un controlador asíncrono, los pasajeros detrás de usted tendrían que esperar años mientras buscaba su dinero, mientras que el conductor del autobús no estaría trabajando.

Entonces, la conclusión es que si muchas personas no saben dónde está su dinero (es decir, requieren mucho tiempo para responder a algo que el conductor ha pedido), los controladores asíncronos podrían ayudar al rendimiento de las solicitudes, acelerando el proceso de algunos. Sin un controlador aysnc, todos esperan hasta que la persona que está al frente haya sido tratada por completo. PERO no olvides que en MVC tienes muchos conductores de autobús en un solo autobús, por lo que async no es una opción automática.

K. Bob
fuente
8
Muy bonita analogía. Gracias.
Pittsburgh DBA
Me gustó la descripción. Gracias
Omer Cansizoglu
Una excelente manera de explicar it.thank,
Anand Vyas
¡Su respuesta en combinación con la respuesta de Darin resume todo el mecanismo detrás de los controladores asíncronos, qué es y, lo que es más importante, qué no es!
Nirman
Esta es una buena analogía, pero mi única pregunta es esta: el chico que hurga en sus bolsillos en su analogía sería algún tipo de trabajo / procesamiento en nuestra aplicación ... entonces, ¿qué procesos funcionan una vez que liberamos el hilo? Malhumorado es otro hilo? Entonces, ¿qué ganamos aquí?
Tomuke
10

Hay dos conceptos en juego aquí. En primer lugar, podemos hacer que nuestro código se ejecute en paralelo para ejecutarlo más rápido o programar el código en otro hilo para evitar que el usuario espere. El ejemplo que tuviste

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

pertenece a la segunda categoría. El usuario obtendrá una respuesta más rápida, pero la carga de trabajo total en el servidor es mayor porque tiene que hacer el mismo trabajo + manejar el subproceso.

Otro ejemplo de esto sería:

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Make async web request to twitter with WebClient.DownloadString()
    });

    Task.Factory.StartNew(() => { 
        //Make async web request to facebook with WebClient.DownloadString()
    });


    //wait for both to be ready and merge the results

    return View();
}

Debido a que las solicitudes se ejecutan en paralelo, el usuario no tendrá que esperar tanto tiempo como si fuera en serie. Pero debe darse cuenta de que usamos más recursos aquí que si lo ejecutamos en serie porque ejecutamos el código en muchos subprocesos mientras también tenemos en espera de subprocesos.

Esto está perfectamente bien en un escenario de cliente. Y es bastante común allí envolver el código síncrono de larga ejecución en una nueva tarea (ejecutarlo en otro hilo) también mantener la interfaz de usuario receptiva o paralizar para que sea más rápido. Sin embargo, todavía se usa un hilo para toda la duración. En un servidor con alta carga, esto podría ser contraproducente porque en realidad usa más recursos. Esto es lo que la gente te ha advertido

Sin embargo, los controladores asíncronos en MVC tienen otro objetivo. El punto aquí es evitar que las sesiones de subprocesos no hagan nada (lo que puede dañar la escalabilidad). Realmente solo importa si las API que está llamando tienen métodos asíncronos. Al igual que WebClient.DowloadStringAsync ().

El punto es que puede permitir que su hilo sea devuelto para manejar nuevas solicitudes hasta que la solicitud web finalice donde lo llamará a la devolución de llamada que recibe el mismo hilo o un nuevo hilo y finalizará la solicitud.

Espero que entiendas la diferencia entre asíncrono y paralelo. Piense en el código paralelo como el código donde se encuentra su hilo y espere el resultado. Si bien el código asincrónico es un código en el que se le notificará cuando se termine el código y pueda volver a trabajar en él, mientras tanto, el hilo puede hacer otro trabajo.

Mikael Eliasson
fuente
6

Las aplicaciones pueden escalar mejor si las operaciones se ejecutan de forma asíncrona, pero solo si hay recursos disponibles para atender las operaciones adicionales .

Las operaciones asincrónicas aseguran que nunca esté bloqueando una acción porque hay una en curso. ASP.NET tiene un modelo asincrónico que permite que se ejecuten múltiples solicitudes en paralelo. Sería posible poner en cola las solicitudes y procesarlas FIFO, pero esto no se escalaría bien cuando tiene cientos de solicitudes en cola y cada solicitud tarda 100 ms en procesarse.

Si usted tiene un gran volumen de tráfico, que puede ser mejor si no realizando sus consultas de forma asíncrona, ya que puede haber recursos adicionales para dar servicio a las peticiones . Si no hay recursos adicionales, sus solicitudes se ven obligadas a hacer cola, tomar exponencialmente más tiempo o fallar por completo, en cuyo caso la sobrecarga asíncrona (mutexes y operaciones de cambio de contexto) no le da nada.

En lo que respecta a ASP.NET, no tiene otra opción: utiliza un modelo asincrónico, porque eso es lo que tiene sentido para el modelo servidor-cliente. Si estuviera escribiendo su propio código internamente que usa un patrón asíncrono para intentar escalar mejor, a menos que esté tratando de administrar un recurso que se comparte entre todas las solicitudes, en realidad no verá ninguna mejora porque ya están envueltas en un proceso asincrónico que no bloquea nada más.

En última instancia, todo es subjetivo hasta que realmente veas qué está causando un cuello de botella en tu sistema. A veces es obvio que un patrón asincrónico ayudará (al evitar un bloqueo de recursos en cola). En última instancia, solo medir y analizar un sistema puede indicar dónde puede ganar eficiencia.

Editar:

En su ejemplo, la Task.Factory.StartNewllamada pondrá en cola una operación en el grupo de subprocesos .NET. La naturaleza de los subprocesos de Thread Pool debe reutilizarse (para evitar el costo de crear / destruir muchos subprocesos). Una vez que se completa la operación, el subproceso se devuelve al grupo para ser reutilizado por otra solicitud (el recolector de basura en realidad no se involucra a menos que haya creado algunos objetos en sus operaciones, en cuyo caso se recopilan según lo normal alcance).

En lo que respecta a ASP.NET, no hay ninguna operación especial aquí. La solicitud ASP.NET se completa sin tener en cuenta la tarea asincrónica. La única preocupación podría ser si su grupo de subprocesos está saturado (es decir, no hay subprocesos disponibles para atender la solicitud en este momento y la configuración del grupo no permite que se creen más subprocesos), en cuyo caso la solicitud se bloquea en espera de iniciar el tarea hasta que un subproceso de grupo esté disponible.

Paul Turner
fuente
¡Gracias! Después de leer su respuesta, edité la pregunta con una muestra de código. ¿Puedes echar un vistazo?
tugberk
Tienes una frase mágica para mí: la Task.Factory.StartNewllamada pondrá en cola una operación en el grupo de subprocesos de .NET. . En este contexto, cuál es el correcto aquí: 1-) Crea un nuevo hilo y cuando se hace, ese hilo vuelve a la agrupación de hilos y espera allí para ser reutilizado nuevamente. 2-) Obtiene un hilo del conjunto de hilos y ese hilo vuelve al conjunto de hilos y espera allí para ser reutilizado nuevamente. 3-) Toma el enfoque más eficiente y puede hacer cualquiera de ellos.
tugberk
1
El grupo de subprocesos crea subprocesos según sea necesario y los recicla cuando no se están utilizando. Su comportamiento exacto ha variado entre las versiones CLR. Puede encontrar información específica al respecto aquí msdn.microsoft.com/en-us/library/0ka9477y.aspx
Paul Turner
Comienza a formarse en mi mente ahora. Entonces, CLR posee el grupo de subprocesos, ¿verdad? Por ejemplo, una aplicación WPF también tiene una noción de grupo de subprocesos y también se ocupa de allí.
tugberk
1
El conjunto de subprocesos es lo suyo dentro del CLR. Otros componentes que son "conscientes" de la agrupación indican que utilizan subprocesos de agrupación de subprocesos (cuando corresponda) en lugar de crear y destruir los suyos. Crear o destruir un subproceso es una operación relativamente costosa, por lo que usar el grupo es una gran ganancia de eficiencia en operaciones de ejecución corta.
Paul Turner el
2

Sí, usan un hilo del grupo de hilos. En realidad, hay una guía bastante excelente de MSDN que abordará todas sus preguntas y más. He encontrado que es bastante útil en el pasado. ¡Echale un vistazo!

http://msdn.microsoft.com/en-us/library/ee728598.aspx

Mientras tanto, los comentarios + sugerencias que escuche sobre el código asincrónico deben tomarse con un grano de sal. Para empezar, solo hacer algo asíncrono no necesariamente mejora la escala, y en algunos casos puede empeorar la escala de su aplicación. El otro comentario que publicó sobre "un gran volumen de tráfico ..." también solo es correcto en ciertos contextos. Realmente depende de lo que estén haciendo sus operaciones y de cómo interactúen con otras partes del sistema.

En resumen, muchas personas tienen muchas opiniones sobre async, pero pueden no ser correctas fuera de contexto. Diría que se centre en sus problemas exactos y realice pruebas de rendimiento básicas para ver qué controladores asíncronos, etc., realmente manejan con su aplicación.

Arkansas
fuente
He leído ese documento tal vez cientos de veces y todavía tengo tanta confusión (tal vez el problema sea yo, quién sabe). Cuando miras a tu alrededor, ves tantos comentarios inconsistentes sobre la asincronía en ASP.NET MVC como puedes ver en mi pregunta.
tugberk
para la última oración: dentro de una acción del controlador, estaba consultando una base de datos 5 veces por separado (tenía que hacerlo) y todo tardaba aproximadamente 400 ms. Luego, implementé AsyncController y los ejecuté en paralelo. El tiempo de respuesta se redujo drásticamente a aprox. 200 ms. Pero no tengo idea de cuántos subprocesos crea, qué sucede con esos subprocesos después de que haya terminado con ellos, GCviene y los limpia justo después de que haya terminado para que mi aplicación no tenga una pérdida de memoria, etc. Alguna idea de esa parte.
tugberk
adjunte un depurador y descúbralo.
AR
0

Lo primero no es MVC sino el IIS que mantiene el grupo de subprocesos. Por lo tanto, cualquier solicitud que venga a la aplicación MVC o ASP.NET se atiende desde hilos que se mantienen en el grupo de hilos. Solo al hacer la aplicación Asynch invoca esta acción en un hilo diferente y libera el hilo inmediatamente para que se puedan tomar otras solicitudes.

He explicado lo mismo con un video detallado ( http://www.youtube.com/watch?v=wvg13n5V0V0/ "Controladores MVC Asynch y falta de hilo") que muestra cómo se produce la falta de hilo en MVC y cómo se minimiza al usar MVC Controladores Asynch. También he medido las colas de solicitud utilizando perfmon para que pueda ver cómo se reducen las colas de solicitud para MVC asynch y cómo es peor para las operaciones de sincronización.

Shivprasad Koirala
fuente