Inicialización única
A menudo, los componentes se diseñan para realizar tareas de inicialización cuando se les llama por primera vez, en lugar de cuando se cargan. Las funciones de inicialización única garantizan que esta inicialización solo se produzca una vez, incluso cuando varios subprocesos intenten la inicialización.
Windows Server 2003 y Windows XP: Las aplicaciones deben proporcionar su propia sincronización para la inicialización única mediante las funciones interbloqueadas u otro mecanismo de sincronización. Las funciones de inicialización única están disponibles a partir de Windows Vista y Windows Server 2008.
Las funciones de inicialización única proporcionan ventajas significativas para asegurar que solo un subproceso realice la inicialización:
- Están optimizados para la velocidad.
- Crean las barreras adecuadas en las arquitecturas de procesador que las requieren.
- Admiten inicialización bloqueada y paralela.
- Evitan el bloqueo interno para que el código pueda funcionar de forma asincrónica o sincrónica.
El sistema administra el proceso de inicialización a través de una estructura de INIT_ONCE opaca que contiene datos e información de estado. El autor de la llamada asigna esta estructura y la inicializa llamando a InitOnceInitialize (para inicializar la estructura dinámicamente) o asignando la constante INIT_ONCE_STATIC_INIT a la variable de estructura (para inicializar la estructura estáticamente). Inicialmente, los datos almacenados en la estructura de inicialización única son NULL y su estado no se inicializa.
Las estructuras de inicialización única no se pueden compartir entre procesos.
El subproceso que realiza la inicialización puede establecer opcionalmente un contexto que esté disponible para el autor de la llamada una vez completada la inicialización. El contexto puede ser un objeto de sincronización o puede ser un valor o una estructura de datos. Si el contexto es un valor, su INIT_ONCE_CTX_RESERVED_BITS de orden bajo debe ser cero. Si el contexto es una estructura de datos, la estructura de datos debe estar alineada con DWORD. El contexto se devuelve al autor de la llamada en el parámetro de salida lpContext de la función InitOnceBeginInitialize o InitOnceExecuteOnce.
La inicialización única se puede realizar de forma sincrónica o asincrónica. Se puede usar una función de devolución de llamada opcional para la inicialización única sincrónica.
Inicialización sincrónica única
En los pasos siguientes se describe la inicialización única sincrónica que no usa una función de devolución de llamada.
- El primer subproceso para llamar a la función InitOnceBeginInitialize hace que se inicie correctamente la inicialización única. Para la inicialización única sincrónica, se debe llamar a InitOnceBeginInitialize sin la marca INIT_ONCE_ASYNC.
- Los subprocesos posteriores que intentan inicializar se bloquean hasta que el primer subproceso complete la inicialización o se produzca un error. Si se produce un error en el primer subproceso, el siguiente puede intentar la inicialización y así sucesivamente.
- Cuando finaliza la inicialización, el subproceso llama a la función InitOnceComplete. El subproceso puede crear opcionalmente un objeto de sincronización (u otros datos de contexto) y especificarlo en el parámetro lpContext de la función InitOnceComplete.
- Si la inicialización se realiza correctamente, el estado de la estructura de inicialización única se cambia a inicializado y el identificador lpContext (si existe) se almacena en la estructura de inicialización. Los intentos de inicialización posteriores devuelven estos datos de contexto. Si se produce un error en la inicialización, los datos son NULL.
En los pasos siguientes se describe la inicialización sincrónica única que usa una función de devolución de llamada.
- El primer subproceso para llamar correctamente a la función InitOnceExecuteOnce pasa un puntero a una función de devolución de llamada InitOnceCallback definida por la aplicación y los datos requeridos por la función de devolución de llamada. Si la llamada se realiza correctamente, se ejecuta la función de devolución de llamada InitOnceCallback.
- Los subprocesos posteriores que intentan inicializar se bloquean hasta que el primer subproceso complete la inicialización o se produzca un error. Si se produce un error en el primer subproceso, el siguiente puede intentar la inicialización y así sucesivamente.
- Cuando finaliza la inicialización, la función de devolución de llamada se devuelve. La función de devolución de llamada puede crear opcionalmente un objeto de sincronización (u otros datos de contexto) y especificarlo en su parámetro de salida Contexto.
- Si la inicialización se realiza correctamente, el estado de la estructura de inicialización única se cambia a inicializado y el controlador de contexto (si existe) se almacena en la estructura de inicialización. Los intentos de inicialización posteriores devuelven estos datos de contexto. Si se produce un error en la inicialización, los datos son NULL.
Inicialización asincrónica única
En los pasos siguientes se describe la inicialización asincrónica única.
- Si varios subprocesos intentan iniciar la inicialización simultáneamente llamando a InitOnceBeginInitialize con INIT_ONCE_ASYNC, la función se realiza correctamente para todos los subprocesos con el parámetro fPending establecido en TRUE. Solo un subproceso se realizará correctamente en la inicialización; otros intentos simultáneos no cambian el estado de inicialización.
- Cuando InitOnceBeginInitialize se devuelve, el parámetro fPending indica el estado de inicialización:
- Si fPending es FALSE, quiere decir que un subproceso se ha realizado correctamente en la inicialización. Otros subprocesos deben limpiar los datos de contexto que hayan creado y usar los datos de contexto en el parámetro de salida lpContext de InitOnceBeginInitialize.
- Si fPending es TRUE, la inicialización aún no se ha completado y otros subprocesos deben continuar.
- Cada subproceso llama a la función InitOnceComplete. El subproceso puede crear opcionalmente un objeto de sincronización (u otros datos de contexto) y especificarlo en el parámetro lpContext de InitOnceComplete.
- Cuando InitOnceComplete devuelve, su valor devuelto indica si el subproceso que realiza la llamada se realizó correctamente al inicializarse.
- Si InitOnceComplete se realiza correctamente, el subproceso que realiza la llamada se ha realizado correctamente en la inicialización. El estado de la estructura de inicialización única se cambia a inicializado y el controlador lpContext (si existe) se almacena en la estructura de inicialización.
- Si se produce un error en InitOnceComplete, otro subproceso se ha realizado correctamente en la inicialización. El subproceso de llamada debe limpiar los datos de contexto que ha creado y llamar a InitOnceBeginInitialize con INIT_ONCE_CHECK_ONLY para recuperar los datos de contexto almacenados en la estructura de inicialización única.
Llamar a la inicialización única desde varios sitios
La inicialización única protegida por una sola estructura de INIT_ONCE se puede realizar desde varios sitios; es posible que se pase una devolución de llamada diferente de cada sitio y se pueda mezclar la sincronización con y sin devolución de llamada. Todavía se garantiza que la inicialización se realice correctamente una sola vez.
Sin embargo, no se pueden mezclar la inicialización asincrónica y la sincrónica: una vez que se intenta la inicialización asincrónica, se producirá un error en los intentos de iniciar la inicialización sincrónica.
Temas relacionados