Inicialização do One-Time

Os componentes geralmente são projetados para executar tarefas de inicialização quando são chamados pela primeira vez, em vez de quando são carregados. As funções de inicialização única garantem que essa inicialização ocorra apenas uma vez, mesmo quando vários threads podem tentar a inicialização.

Windows Server 2003 e Windows XP: Os aplicativos devem fornecer sua própria sincronização para inicialização única usando as funções interligadas ou outro mecanismo de sincronização. As funções de inicialização única estão disponíveis a partir do Windows Vista e do Windows Server 2008.

As funções de inicialização única fornecem vantagens significativas para garantir que apenas um thread execute a inicialização:

  • Eles são otimizados para velocidade.
  • Eles criam as barreiras apropriadas em arquiteturas de processador que as exigem.
  • Eles dão suporte à inicialização bloqueada e paralela.
  • Eles evitam o bloqueio interno para que o código possa operar de forma assíncrona ou síncrona.

O sistema gerencia o processo de inicialização por meio de uma estrutura de INIT_ONCE opaca que contém dados e informações de estado. O chamador aloca essa estrutura e a inicializa chamando InitOnceInitialize (para inicializar a estrutura dinamicamente) ou atribuindo a constante INIT_ONCE_STATIC_INIT à variável de estrutura (para inicializar a estrutura estaticamente). Inicialmente, os dados armazenados na estrutura de inicialização única são NULL e seu estado não é inicializado.

Estruturas de inicialização única não podem ser compartilhadas entre processos.

O thread que executa a inicialização pode, opcionalmente, definir um contexto que está disponível para o chamador após a conclusão da inicialização. O contexto pode ser um objeto de sincronização ou pode ser um valor ou uma estrutura de dados. Se o contexto for um valor, sua INIT_ONCE_CTX_RESERVED_BITS de ordem baixa deverá ser zero. Se o contexto for uma estrutura de dados, a estrutura de dados deverá ser alinhada a DWORD. O contexto é retornado ao chamador no parâmetro de saída lpContext da função InitOnceBeginInitialize ou InitOnceExecuteOnce .

A inicialização única pode ser executada de forma síncrona ou assíncrona. Uma função de retorno de chamada opcional pode ser usada para inicialização única síncrona.

Inicialização única síncrona

As etapas a seguir descrevem a inicialização única síncrona que não usa uma função de retorno de chamada.

  1. O primeiro thread a chamar a função InitOnceBeginInitialize faz com que a inicialização única seja iniciada com êxito. Para inicialização única síncrona, InitOnceBeginInitialize deve ser chamado sem o sinalizador INIT_ONCE_ASYNC .
  2. Threads subsequentes que tentam inicialização são bloqueados até que o primeiro thread conclua a inicialização ou falhe. Se o primeiro thread falhar, o próximo thread poderá tentar a inicialização e assim por diante.
  3. Quando a inicialização é concluída, o thread chama a função InitOnceComplete . Opcionalmente, o thread pode criar um objeto de sincronização (ou outros dados de contexto) e especificá-lo no parâmetro lpContext da função InitOnceComplete .
  4. Se a inicialização for bem-sucedida, o estado da estrutura de inicialização única será alterado para inicializado e o identificador lpContext (se houver) será armazenado na estrutura de inicialização. As tentativas de inicialização subsequentes retornam esses dados de contexto. Se a inicialização falhar, os dados serão NULL.

As etapas a seguir descrevem a inicialização única síncrona que usa uma função de retorno de chamada.

  1. O primeiro thread a chamar com êxito a função InitOnceExecuteOnce passa um ponteiro para uma função de retorno de chamada InitOnceCallback definida pelo aplicativo e todos os dados exigidos pela função de retorno de chamada. Se a chamada for bem-sucedida, a função de retorno de chamada InitOnceCallback será executada.
  2. Threads subsequentes que tentam inicialização são bloqueados até que o primeiro thread conclua a inicialização ou falhe. Se o primeiro thread falhar, o próximo thread poderá tentar a inicialização e assim por diante.
  3. Quando a inicialização for concluída, a função de retorno de chamada retornará. Opcionalmente, a função de retorno de chamada pode criar um objeto de sincronização (ou outros dados de contexto) e especificá-lo em seu parâmetro de saída Context .
  4. Se a inicialização for bem-sucedida, o estado da estrutura de inicialização única será alterado para inicializado e o identificador de contexto (se houver) será armazenado na estrutura de inicialização. As tentativas de inicialização subsequentes retornam esses dados de contexto. Se a inicialização falhar, os dados serão NULL.

Inicialização única assíncrona

As etapas a seguir descrevem a inicialização única assíncrona.

  1. Se vários threads tentarem iniciar a inicialização simultaneamente chamando InitOnceBeginInitialize com INIT_ONCE_ASYNC, a função terá êxito em todos os threads com o parâmetro fPending definido como TRUE. Apenas um thread terá êxito na inicialização; outras tentativas simultâneas não alteram o estado de inicialização.
  2. Quando InitOnceBeginInitialize retorna, o parâmetro fPending indica o status de inicialização:
    • Se fPending for FALSE, um thread terá êxito na inicialização. Outros threads devem limpo todos os dados de contexto que eles criaram e usar os dados de contexto no parâmetro de saída lpContext de InitOnceBeginInitialize.
    • Se fPending for TRUE, a inicialização ainda não foi concluída e outros threads deverão continuar.
  3. Cada thread chama a função InitOnceComplete . Opcionalmente, o thread pode criar um objeto de sincronização (ou outros dados de contexto) e especificá-lo no parâmetro lpContext de InitOnceComplete.
  4. Quando InitOnceComplete retorna, seu valor retornado indica se o thread de chamada foi bem-sucedido na inicialização.
    • Se InitOnceComplete for bem-sucedido , o thread de chamada terá êxito na inicialização. O estado da estrutura de inicialização única é alterado para inicializado e o identificador lpContext (se houver) é armazenado na estrutura de inicialização.
    • Se InitOnceComplete falhar, outro thread terá êxito na inicialização. O thread de chamada deve limpo todos os dados de contexto criados e chamar InitOnceBeginInitialize com INIT_ONCE_CHECK_ONLY para recuperar os dados de contexto armazenados na estrutura de inicialização única.

Chamando One-Time inicialização de vários sites

A inicialização única protegida por uma única estrutura de INIT_ONCE pode ser executada em sites de mutiple; um retorno de chamada diferente pode ser passado de cada site e a sincronização com e sem retorno de chamada pode ser misturada. A inicialização ainda é assegurada para ser executada com êxito apenas uma vez.

No entanto, a inicialização assíncrona e síncrona não pode ser misturada: depois que a inicialização assíncrona for tentada, as tentativas de iniciar a inicialização síncrona falharão.

Usando a inicialização de One-Time