Partager via


Initialisation ponctuelle

Les composants sont souvent conçus pour effectuer des tâches d’initialisation lorsqu’ils sont appelés pour la première fois, plutôt que lorsqu’ils sont chargés. Les fonctions d’initialisation ponctuelle garantissent que cette initialisation ne se produit qu’une seule fois, même lorsque plusieurs threads peuvent tenter de procéder à l’initialisation.

Windows Server 2003 et Windows XP : les applications doivent fournir leur propre synchronisation pour l’initialisation ponctuelle à l’aide des fonctions verrouillées ou d’un autre mécanisme de synchronisation. Les fonctions d’initialisation ponctuelle sont disponibles à partir de Windows Vista et Windows Server 2008.

Les fonctions d’initialisation ponctuelle offrent des avantages significatifs pour s’assurer qu’un seul thread effectue l’initialisation :

  • Elles sont optimisées pour la vitesse.
  • Elles créent les obstacles appropriés sur les architectures de processeur qui les nécessitent.
  • Elles prennent en charge l’initialisation verrouillée et l’initialisation parallèle.
  • Elles évitent le verrouillage interne, afin que le code puisse fonctionner de manière asynchrone ou synchrone.

Le système gère le processus d’initialisation par le biais d’une structure INIT_ONCE opaque qui contient des informations sur les données et sur l’état. L’appelant alloue cette structure et l’initialise en appelant InitOnceInitialize (pour initialiser la structure dynamiquement) ou en affectant la constante INIT_ONCE_STATIC_INIT à la variable de structure (pour initialiser la structure statiquement). Initialement, les données stockées dans la structure d’initialisation ponctuelle sont Null et leur état est non initialisé.

Les structures d’initialisation ponctuelle ne peuvent pas être partagées entre les processus.

Le thread qui effectue l’initialisation peut éventuellement définir un contexte disponible pour l’appelant une fois l’initialisation terminée. Le contexte peut être un objet de synchronisation, ou il peut s’agir d’une valeur ou d’une structure de données. Si le contexte est une valeur, son INIT_ONCE_CTX_RESERVED_BITS de poids faible doit être égal à zéro. Si le contexte est une structure de données, celle-ci doit être alignée sur DWORD. Le contexte est retourné à l’appelant dans le paramètre de sortie lpContext de la fonction InitOnceBeginInitialize ou InitOnceExecuteOnce.

L’initialisation ponctuelle peut être effectuée de manière synchrone ou asynchrone. Une fonction de rappel facultative peut être utilisée pour l’initialisation ponctuelle synchrone.

Initialisation ponctuelle synchrone

Les étapes suivantes décrivent l’initialisation ponctuelle synchrone qui n’utilise pas de fonction de rappel.

  1. Le premier thread à appeler la fonction InitOnceBeginInitialize entraîne le début de l’initialisation ponctuelle. Pour l’initialisation ponctuelle synchrone, InitOnceBeginInitialize doit être appelée sans l’indicateur INIT_ONCE_ASYNC.
  2. Les threads suivants qui tentent de procéder à l’initialisation sont bloqués jusqu’à ce que le premier thread termine l’initialisation ou échoue. Si le premier thread échoue, le thread suivant est autorisé à tenter l’initialisation, et ainsi de suite.
  3. Une fois l’initialisation terminée, le thread appelle la fonction InitOnceComplete. Le thread peut éventuellement créer un objet de synchronisation (ou d’autres données de contexte) et le spécifier dans le paramètre lpContext de la fonction InitOnceComplete.
  4. Si l’initialisation réussit, l’état de la structure d’initialisation ponctuelle passe à « Initialisé » et le handle lpContext (le cas échéant) est stocké dans la structure d’initialisation. Les tentatives d’initialisation suivantes retournent ces données de contexte. Si l’initialisation échoue, les données sont Null.

Les étapes suivantes décrivent l’initialisation ponctuelle synchrone qui utilise une fonction de rappel.

  1. Le premier thread qui appelle avec succès la fonction InitOnceExecuteOnce transmet un pointeur vers une fonction de rappel InitOnceCallback définie par l’application et toutes les données requises par la fonction de rappel. Si l’appel réussit, la fonction de rappel InitOnceCallback s’exécute.
  2. Les threads suivants qui tentent de procéder à l’initialisation sont bloqués jusqu’à ce que le premier thread termine l’initialisation ou échoue. Si le premier thread échoue, le thread suivant est autorisé à tenter l’initialisation, et ainsi de suite.
  3. Une fois l’initialisation terminée, la fonction de rappel retourne. La fonction de rappel peut éventuellement créer un objet de synchronisation (ou d’autres données de contexte) et le spécifier dans son paramètre de sortie Context.
  4. Si l’initialisation réussit, l’état de la structure d’initialisation ponctuelle passe à « Initialisé » et le handle Context (le cas échéant) est stocké dans la structure d’initialisation. Les tentatives d’initialisation suivantes retournent ces données de contexte. Si l’initialisation échoue, les données sont Null.

Initialisation ponctuelle asynchrone

Les étapes suivantes décrivent l’initialisation ponctuelle asynchrone.

  1. Si plusieurs threads tentent simultanément de commencer l’initialisation en appelant InitOnceBeginInitialize avec INIT_ONCE_ASYNC, la fonction réussit pour tous les threads dont le paramètre fPendinga la valeur TRUE. En réalité, un seul thread réussira à procéder à l’initialisation ; les autres tentatives simultanées ne changent pas l’état d’initialisation.
  2. Lorsque InitOnceBeginInitialize retourne, le paramètre fPending indique l’état d’initialisation :
    • Si fPending a la valeur FALSE, cela signifie qu’un thread a réussi l’initialisation. Les autres threads doivent nettoyer toutes les données de contexte qu’ils ont créées et utiliser les données de contexte dans le paramètre de sortie lpContext d’InitOnceBeginInitialize.
    • Si fPending a la valeur TRUE, cela signifie que l’initialisation n’est pas encore terminée et que d’autres threads doivent continuer.
  3. Chaque thread appelle la fonction InitOnceComplete. Le thread peut éventuellement créer un objet de synchronisation (ou d’autres données de contexte) et le spécifier dans le paramètre lpContext d’InitOnceComplete.
  4. Lorsque InitOnceComplete retourne, sa valeur de retour indique si le thread appelant a réussi à effectuer l’initialisation.
    • Si InitOnceComplete réussit, cela signifie que le thread appelant a réussi à effectuer l’initialisation. L’état de la structure d’initialisation ponctuelle passe à « Initialisé » et le handle lpContext (le cas échéant) est stocké dans la structure d’initialisation.
    • Si InitOnceComplete échoue, cela signifie qu’un autre thread a réussi à effectuer l’initialisation. Le thread appelant doit nettoyer toutes les données de contexte qu’il a créées et appeler InitOnceBeginInitialize avec INIT_ONCE_CHECK_ONLY pour récupérer les données de contexte stockées dans la structure d’initialisation ponctuelle.

Appel de l’initialisation ponctuelle à partir de plusieurs sites

Une initialisation ponctuelle protégée par une structure INIT_ONCE unique peut être effectuée à partir de plusieurs sites ; un rappel différent peut être transmis à partir de chaque site, et la synchronisation avec et sans rappel peut être combinée. Il est toujours garanti que l’initialisation ne réussira qu’une seule fois.

Toutefois, l’initialisation asynchrone et synchrone ne peut pas être combinée : une fois l’initialisation asynchrone tentée, les tentatives de démarrage de l’initialisation synchrone échouent.

Utilisation de l’initialisation ponctuelle