Dela via


One-Time initiering

Komponenter är ofta utformade för att utföra initieringsuppgifter när de först anropas, i stället för när de läses in. Engångsinitieringsfunktionerna säkerställer att den här initieringen endast sker en gång, även om flera trådar kan försöka initiera.

Windows Server 2003 och Windows XP: Program måste tillhandahålla en egen synkronisering för engångsinitiering med hjälp av sammankopplade funktioner eller någon annan synkroniseringsmekanism. Engångsinitieringsfunktionerna är tillgängliga från och med Windows Vista och Windows Server 2008.

Engångsinitieringsfunktionerna ger betydande fördelar för att säkerställa att endast en tråd utför initieringen:

  • De är optimerade för hastighet.
  • De skapar lämpliga hinder för processorarkitekturer som kräver dem.
  • De stöder både låst och parallell initiering.
  • De undviker intern låsning så att koden kan fungera asynkront eller synkront.

Systemet hanterar initieringsprocessen genom en ogenomskinlig INIT_ONCE struktur som innehåller data och tillståndsinformation. Anroparen allokerar den här strukturen och initierar den genom att antingen anropa InitOnceInitialize (för att initiera strukturen dynamiskt) eller tilldela konstanten INIT_ONCE_STATIC_INIT till strukturvariabeln (för att initiera strukturen statiskt). Initialt är data som lagras i engångsinitieringsstrukturen NULL och dess tillstånd är onitialiserat.

Engångsinitieringsstrukturer kan inte delas mellan processer.

Tråden som utför initieringen kan också ange en kontext som är tillgänglig för anroparen när initieringen är klar. Kontexten kan vara ett synkroniseringsobjekt eller ett värde eller en datastruktur. Om kontexten är ett värde måste dess INIT_ONCE_CTX_RESERVED_BITS med låg ordning vara noll. Om kontexten är en datastruktur måste datastrukturen vara DWORD--justerad. Kontexten returneras till anroparen i utdataparametern lpContext för funktionen InitOnceBeginInitialize eller InitOnceExecuteOnce.

Engångsinitiering kan utföras synkront eller asynkront. En valfri återanropsfunktion kan användas för synkron engångsinitiering.

Synkron engångsinitiering

Följande steg beskriver synkron engångsinitiering som inte använder en återanropsfunktion.

  1. Den första tråden som anropar funktionen InitOnceBeginInitialize gör att en engångsinitiering påbörjas. För synkron engångsinitiering måste InitOnceBeginInitialize anropas utan flaggan INIT_ONCE_ASYNC.
  2. Efterföljande trådar som försöker initiera blockeras tills den första tråden antingen slutför initieringen eller misslyckas. Om den första tråden misslyckas tillåts nästa tråd att försöka initiera och så vidare.
  3. När initieringen är klar anropar tråden funktionen InitOnceComplete. Tråden kan också skapa ett synkroniseringsobjekt (eller andra kontextdata) och ange det i parametern lpContext för funktionen InitOnceComplete.
  4. Om initieringen lyckas ändras tillståndet för engångsinitieringsstrukturen till initierad och lpContext- handtag (om någon) lagras i initieringsstrukturen. Efterföljande initieringsförsök returnerar dessa kontextdata. Om initieringen misslyckas är data NULL-.

Följande steg beskriver synkron engångsinitiering som använder en återanropsfunktion.

  1. Den första tråden som anropar funktionen InitOnceExecuteOnce skickar en pekare till en programdefinierad InitOnceCallback återanropsfunktion och alla data som krävs av återanropsfunktionen. Om anropet lyckas körs funktionen InitOnceCallback motringning.
  2. Efterföljande trådar som försöker initiera blockeras tills den första tråden antingen slutför initieringen eller misslyckas. Om den första tråden misslyckas tillåts nästa tråd att försöka initiera och så vidare.
  3. När initieringen är klar returnerar återanropsfunktionen. Återanropsfunktionen kan också skapa ett synkroniseringsobjekt (eller andra kontextdata) och ange det i sin Kontext utdataparameter.
  4. Om initieringen lyckas ändras tillståndet för engångsinitieringsstrukturen till initierad och Context-handtag (om någon) lagras i initieringsstrukturen. Efterföljande initieringsförsök returnerar dessa kontextdata. Om initieringen misslyckas är data NULL-.

Asynkron engångsinitiering

Följande steg beskriver asynkron engångsinitiering.

  1. Om flera trådar försöker starta initieringen samtidigt genom att anropa InitOnceBeginInitialize med INIT_ONCE_ASYNClyckas funktionen för alla trådar med parametern fPending inställd på TRUE. Endast en tråd lyckas faktiskt vid initieringen. andra samtidiga försök ändrar inte initieringstillståndet.
  2. När InitOnceBeginInitialize returnerar anger parametern fPending initieringsstatus:
    • Om fPending är FALSEhar en tråd lyckats vid initieringen. Andra trådar bör rensa kontextdata som de har skapat och använda kontextdata i lpContext utdataparametern för InitOnceBeginInitialize.
    • Om fPending är TRUEhar initieringen ännu inte slutförts och andra trådar bör fortsätta.
  3. Varje tråd anropar funktionen InitOnceComplete. Tråden kan också skapa ett synkroniseringsobjekt (eller andra kontextdata) och ange det i parametern lpContext för InitOnceComplete.
  4. När InitOnceComplete- returnerar anger dess returvärde om den anropande tråden lyckades vid initieringen.
    • Om InitOnceComplete lyckas har den anropande tråden lyckats vid initieringen. Tillståndet för engångsinitieringsstrukturen ändras till initierad och lpContext- handtag (om någon) lagras i initieringsstrukturen.
    • Om InitOnceComplete- misslyckas har en annan tråd lyckats vid initieringen. Den anropande tråden bör rensa alla kontextdata som den har skapat och anropa InitOnceBeginInitialize med INIT_ONCE_CHECK_ONLY för att hämta kontextdata som lagras i enstaka initieringsstruktur.

Anropa One-Time initiering från flera platser

Engångsinitiering som skyddas av en enda INIT_ONCE struktur kan utföras från flera platser. olika motringningar kan skickas från varje plats och synkronisering med och utan återanrop kan blandas. Initieringen är fortfarande garanterad att utföras bara en gång.

Det går dock inte att blanda asynkron och synkron initiering: när asynkron initiering har försökt startas misslyckas försök att starta synkron initiering.

Använda One-Time initiering