Compartir a través de


Puertos de finalización de E/S

Los puertos de finalización de E/S proporcionan un modelo de subproceso eficaz para procesar varias solicitudes de E/S asincrónicas en un sistema de varios procesadores. Cuando un proceso crea un puerto de finalización de E/S, el sistema crea un objeto de cola asociado para subprocesos cuyo único propósito es atender estas solicitudes. Los procesos que controlan muchas solicitudes de E/S asincrónicas simultáneas pueden hacerlo de forma más rápida y eficaz mediante el uso de puertos de finalización de E/S junto con un grupo de subprocesos asignado previamente que mediante la creación de subprocesos en el momento en que reciben una solicitud de E/S.

Funcionamiento de los puertos de finalización de E/S

La función CreateIoCompletionPort crea un puerto de finalización de E/S y asocia uno o varios identificadores de archivo a ese puerto. Cuando se completa una operación de E/S asincrónica en uno de estos identificadores de archivo, se encola un paquete de finalización de E/S en orden de llegada (FIFO) al puerto de finalización de E/S asociado. Un uso eficaz para este mecanismo es combinar el punto de sincronización para varios identificadores de archivo en un solo objeto, aunque también hay otras aplicaciones útiles. Tenga en cuenta que, aunque los paquetes se ponen en cola en el orden FIFO, pueden ser desencolados en un orden diferente.

Nota:

El término identificador de archivo que se usa aquí hace referencia a una abstracción del sistema que representa un punto de conexión de E/S superpuesto, no solo un archivo en el disco. Por ejemplo, puede ser un punto de conexión de red, un socket TCP, una canalización con nombre o una ranura de correo. Se puede usar cualquier objeto del sistema que admita E/S superpuesta. Para obtener una lista de las funciones de E/S relacionadas, consulte el final de este tema.

Cuando un identificador de archivo está asociado a un puerto de finalización, el bloque de estado pasado no se actualizará hasta que el paquete se quite del puerto de finalización. La única excepción es si la operación original devuelve de forma sincrónica un error. Un subproceso (ya sea uno creado por el subproceso principal o el propio subproceso principal) usa la función GetQueuedCompletionStatus para esperar a que se ponga en cola un paquete de finalización al puerto de finalización de E/S, en lugar de esperar directamente a que se complete la E/S asincrónica. Los subprocesos que bloquean su ejecución en un puerto de finalización de E/S se liberan en el orden del último en salir (LIFO) y el siguiente paquete de finalización se extrae de la cola FIFO del puerto de finalización de E/S para ese subproceso. Esto significa que, cuando se libera un paquete de finalización en un subproceso, el sistema libera el último subproceso (más reciente) asociado a ese puerto, pasando la información de finalización para la operación de E/S más antigua.

Aunque cualquier número de subprocesos puede llamar a GetQueuedCompletionStatus para un puerto de finalización de E/S especificado, cuando un subproceso especificado llama a GetQueuedCompletionStatus la primera vez, se asocia al puerto de finalización de E/S especificado hasta que se produce una de estas tres cosas: el subproceso sale, especifica un puerto de finalización de E/S diferente o cierra el puerto de finalización de E/S especificado. Es decir, un único subproceso se puede asociar, como máximo, a un puerto de finalización de E/S.

Cuando un paquete de finalización se pone en cola en un puerto de finalización de E/S, el sistema comprueba primero cuántos subprocesos asociados a ese puerto se están ejecutando. Si el número de subprocesos que se ejecutan es menor que el valor de simultaneidad (descrito en la sección siguiente), se permite que uno de los subprocesos en espera (el más reciente) procese el paquete de finalización. Cuando un subproceso en ejecución completa su procesamiento, normalmente llama a GetQueuedCompletionStatus de nuevo, en cuyo punto vuelve con el siguiente paquete de finalización o espera si la cola está vacía.

Los subprocesos pueden usar la función PostQueuedCompletionStatus para colocar paquetes de finalización en la cola de un puerto de finalización de E/S. Al hacerlo, el puerto de finalización se puede usar para recibir comunicaciones de otros subprocesos del proceso, además de recibir paquetes de finalización de E/S del sistema de E/S. La función PostQueuedCompletionStatus permite a una aplicación poner en cola sus propios paquetes de finalización de propósito especial al puerto de finalización de E/S sin iniciar una operación de E/S asincrónica. Esto resulta útil para notificar a los subprocesos de trabajo de eventos externos, por ejemplo.

El identificador de puerto de finalización de E/S y todos los controladores de archivos asociados a ese puerto de finalización de E/S concreto se conocen como referencias al puerto de finalización de E/S. El puerto de finalización de E/S se libera cuando ya no existen más referencias al mismo. Por lo tanto, todos estos identificadores deben cerrarse correctamente para liberar el puerto de finalización de E/S y sus recursos del sistema asociados. Una vez cumplidas estas condiciones, una aplicación debe cerrar el identificador del puerto de finalización de E/S llamando a la función CloseHandle.

Nota:

Un puerto de finalización de E/S está asociado al proceso que lo creó y no se puede compartir entre procesos. Sin embargo, un único identificador se puede compartir entre subprocesos en el mismo proceso.

Hilos y concurrencia

La propiedad más importante de un puerto de finalización de E/S que se debe considerar detenidamente es el valor de concurrencia. El valor de simultaneidad de un puerto de finalización se especifica cuando se crea con CreateIoCompletionPort mediante el parámetro NumberOfConcurrentThreads . Este valor limita el número de subprocesos ejecutables asociados al puerto de finalización. Cuando el número total de subprocesos ejecutables asociados con el puerto de finalización alcanza el valor de simultaneidad, el sistema bloquea la ejecución de cualquier subproceso posterior asociado a ese puerto de finalización hasta que el número de subprocesos ejecutables cae por debajo del valor de simultaneidad.

El escenario más eficiente se produce cuando hay paquetes de finalización esperando en la cola, pero no se pueden satisfacer las esperas porque el puerto ha alcanzado su límite de concurrencia. Tenga en cuenta lo que sucede con un valor de simultaneidad de uno y varios subprocesos que esperan en la llamada a la función GetQueuedCompletionStatus . En este caso, si la cola siempre tiene paquetes de finalización en espera, cuando el subproceso en ejecución llama a GetQueuedCompletionStatus, no bloqueará la ejecución porque, como se mencionó anteriormente, la cola de subprocesos es LIFO. En su lugar, este hilo recogerá inmediatamente el siguiente paquete de finalización que esté en cola. No se producirán cambios de contexto de subproceso, porque el subproceso en ejecución recoge continuamente paquetes de finalización de tareas, impidiendo que los demás subprocesos puedan ejecutarse.

Nota:

En el ejemplo anterior, los subprocesos adicionales parecen ser inútiles y nunca se ejecutan, pero eso supone que el subproceso en ejecución nunca se pone en un estado de espera por algún otro mecanismo, finaliza o cierra su puerto de finalización de E/S asociado. Tenga en cuenta todas estas ramificaciones de ejecución de subprocesos al diseñar la aplicación.

El mejor valor máximo general que se debe elegir para el valor de simultaneidad es el número de CPU del equipo. Si la transacción requiere un cálculo largo, un valor de simultaneidad mayor permitirá que se ejecuten más subprocesos. Cada paquete de finalización puede tardar más tiempo en finalizar, pero se procesarán más paquetes de finalización al mismo tiempo. Puede experimentar con el valor de simultaneidad junto con las herramientas de generación de perfiles para lograr el mejor efecto para la aplicación.

El sistema también permite que un subproceso que espere en GetQueuedCompletionStatus procese un paquete de finalización si otro subproceso en ejecución asociado al mismo puerto de finalización de E/S entra en un estado de espera por otros motivos, por ejemplo, la función SuspendThread . Cuando el subproceso del estado de espera comienza a ejecutarse de nuevo, puede haber un breve período cuando el número de subprocesos activos supere el valor de simultaneidad. Sin embargo, el sistema reduce rápidamente este número al no permitir ningún nuevo subproceso activo hasta que el número de subprocesos activos se encuentra por debajo del valor de simultaneidad. Este es un motivo para que la aplicación cree más subprocesos en su grupo de subprocesos que el valor de simultaneidad. La administración del grupo de subprocesos está fuera del ámbito de este tema, pero una buena regla general es tener al menos el doble de subprocesos en el grupo de subprocesos que procesadores hay en el sistema. Para más información sobre la agrupación de subprocesos, consulte Grupos de subprocesos.

Funciones de E/S compatibles

Las siguientes funciones se pueden usar para iniciar operaciones de E/S que se completan mediante puertos de finalización de E/S. Debe pasar a la función una instancia de la estructura OVERLAPPED y un identificador de archivo asociado previamente a un puerto de finalización de E/S (mediante una llamada a CreateIoCompletionPort) para habilitar el mecanismo de puerto de finalización de E/S:

Acerca de procesos y subprocesos

BindIoCompletionCallback

CreateIoCompletionPort