Descripción simple de los subprocesos de trabajo y de E/S en.NET
multithreading iocp (4)
Es muy difícil encontrar una descripción detallada pero simple de los hilos de trabajo y de E / S en .NET
Lo que tengo claro con respecto a este tema (pero puede que no sea técnicamente preciso):
- Las hebras de trabajo son hebras que deberían emplear CPU para su trabajo;
- Los subprocesos de E / S (también denominados "subprocesos de puerto de finalización") deben emplear controladores de dispositivo para su trabajo y esencialmente "no hacer nada", solo supervisar la finalización de las operaciones sin CPU.
Lo que no está claro:
- Aunque el método ThreadPool.GetAvailableThreads devuelve el número de subprocesos disponibles de ambos tipos, parece que no hay una API pública para programar el trabajo para el subproceso de E / S. ¿Solo puede crear manualmente un subproceso de trabajo en .NET?
- Parece que un solo hilo de E / S puede monitorear múltiples operaciones de E / S. ¿Es verdad? Si es así, ¿por qué ThreadPool tiene tantos hilos de E / S disponibles por defecto?
- En algunos textos, leí esa devolución de llamada, que se activa después de que la terminación de la operación de E / S se realiza mediante un hilo de E / S. ¿Es verdad? ¿No es este un trabajo para un subproceso de trabajo, teniendo en cuenta que esta devolución de llamada es una operación de la CPU?
- Para ser más específicos, ¿las páginas asíncronas de ASP.NET subprocesos de E / S del usuario? ¿Cuál es exactamente el beneficio de rendimiento al cambiar el trabajo de E / S a subprocesos separados en lugar de aumentar el número máximo de subprocesos de trabajo? ¿Es porque un solo hilo de E / S monitorea múltiples operaciones? ¿O Windows hace un cambio de contexto más eficiente cuando se usan hilos de E / S?
Alguien con más habilidades que yo va a saltar aquí para ayudar.
Los subprocesos de trabajo tienen mucho estado, están programados por el procesador, etc. y usted controla todo lo que hacen.
Los puertos de finalización de IO son proporcionados por el sistema operativo para tareas muy específicas que involucran poco estado compartido, y por lo tanto son más rápidos de usar. Un buen ejemplo en .Net es el framework WCF. Cada "llamada" a un servicio WCF en realidad es ejecutada por un Puerto de finalización de E / S porque son los más rápidos de iniciar y el sistema operativo los busca por usted.
Comenzaré con una descripción de cómo los programas en NT utilizan la E / S asíncrona.
Puede estar familiarizado con la función de la API de Win32, ReadFile (como ejemplo), que es un contenedor alrededor de la función de la API nativa NtReadFile . Esta función le permite hacer dos cosas con E / S asíncrona:
- Puede crear un objeto de evento y pasarlo a NtReadFile . Este evento se señalizará cuando finalice la operación de lectura.
- Puede pasar una función de llamada de procedimiento asíncrono (APC) a NtReadFile . Esencialmente, lo que esto significa es que cuando finalice la operación de lectura, la función se pondrá en cola en el subproceso que inició la operación y se ejecutará cuando el subproceso realice una espera de alerta .
Sin embargo, existe una tercera forma de recibir notificaciones cuando se completa una operación de E / S. Puede crear un objeto de puerto de finalización de E / S y asociar los identificadores de archivo con él. Cuando una operación se completa en un archivo que está asociado con el puerto de finalización de E / S, los resultados de la operación (como el estado de E / S) se ponen en cola en el puerto de finalización de E / S. Luego, puede configurar un hilo dedicado para eliminar los resultados de la cola y realizar las tareas adecuadas, como llamar a las funciones de devolución de llamada. Esto es esencialmente lo que es un "subproceso de trabajo de E / S".
Un "hilo de trabajo" normal es muy similar; en lugar de eliminar los resultados de E / S de una cola, elimina los elementos de trabajo de una cola. Puede poner en cola los elementos de trabajo ( QueueUserWorkItem ) y hacer que los subprocesos de trabajo los ejecuten. Esto evita que tenga que generar un hilo cada vez que quiera realizar una tarea de forma asíncrona.
El simple hecho de poner un subproceso de trabajo está destinado a realizar un corto período de trabajo y se eliminará cuando se complete. Se puede usar una devolución de llamada para notificar al proceso principal que se ha completado o para devolver datos.
Un subproceso de E / S realizará la misma operación o serie de operaciones continuamente hasta que el proceso principal lo detenga. Se llama así porque, por lo general, los controladores de dispositivo se ejecutan de forma continua y controlan el puerto del dispositivo. Un hilo de E / S normalmente creará eventos cuando desee comunicarse con otros hilos.
Todos los procesos se ejecutan como hilos. Su aplicación se ejecuta como un hilo. Cualquier subproceso puede generar subprocesos de trabajo o subprocesos de E / S (como los llama).
Siempre hay un buen equilibrio entre el rendimiento y la cantidad o el tipo de subprocesos utilizados. Demasiadas devoluciones de llamadas o eventos gestionados por un proceso degradarán gravemente su rendimiento debido a la cantidad de interrupciones en su ciclo de proceso principal a medida que las maneja.
Los ejemplos de un subproceso de trabajo serían agregar datos a una base de datos después de la interacción del usuario o realizar un cálculo matemático largo o escribir datos en un archivo. Al utilizar un subproceso de trabajo, libera la aplicación principal, esto es más útil para las GUI, ya que no se congela mientras se realiza la tarea.
El término ''subproceso de trabajo'' en .net / CLR generalmente solo se refiere a cualquier subproceso que no sea el subproceso principal que hace algún ''trabajo'' en nombre de la aplicación que generó el subproceso. "Trabajo" realmente podría significar cualquier cosa, incluso esperar a que se complete alguna E / S. ThreadPool mantiene un caché de subprocesos de trabajo porque los subprocesos son costosos de crear.
El término ''subproceso de E / S'' en .net / CLR se refiere a los subprocesos que ThreadPool reserva para enviar devoluciones de llamadas nativas superpuestas de llamadas win32 "superpuestas" (también conocidas como "puerto E / S de finalización"). El CLR mantiene su propio puerto de finalización de E / S, y puede enlazar cualquier identificador a él (a través de la API ThreadPool.BindHandle). Ejemplo aquí: http://blogs.msdn.com/junfeng/archive/2008/12/01/threadpool-bindhandle.aspx . Muchas API de .net usan este mecanismo internamente para recibir devoluciones de llamadas de NativeOverlapped, aunque el desarrollador típico de .net nunca lo usará directamente.
Realmente no hay diferencia técnica entre ''subproceso de trabajo'' y ''subproceso de E / S'', ambos son solo hilos normales. Pero el CLR ThreadPool mantiene grupos separados de cada uno simplemente para evitar una situación en la que la alta demanda de subprocesos de trabajo agote todos los subprocesos disponibles para enviar devoluciones de llamadas de E / S nativas, lo que podría provocar un interbloqueo. (Imagine una aplicación que utiliza los 250 subprocesos de trabajo, donde cada uno está esperando que se complete una E / S).
El desarrollador debe tener cuidado al manejar una devolución de llamada de E / S para garantizar que el subproceso de E / S se devuelva a ThreadPool, es decir, el código de devolución de llamada de E / S debe hacer el trabajo mínimo requerido para dar servicio a la devolución de llamada y luego devolver el control del hilo a la agrupación de hilos CLR. Si se requiere más trabajo, ese trabajo debe programarse en un subproceso de trabajo. De lo contrario, la aplicación corre el riesgo de "secuestrar" el conjunto de subprocesos de finalización de E / S reservados de CLR para su uso como subprocesos de trabajo normales, lo que lleva a la situación de interbloqueo descrita anteriormente.
Algunas buenas referencias para lecturas adicionales: puertos de finalización de E / S de win32: http://msdn.microsoft.com/en-us/library/aa365198(VS.85).aspx threadpool gestionado: http://msdn.microsoft.com/en-us/library/0ka9477y.aspx ejemplo de BindHandle: http://blogs.msdn.com/junfeng/archive/2008/12/01/threadpool-bindhandle.aspx