inizializzazione One-Time

I componenti sono spesso progettati per eseguire attività di inizializzazione quando vengono chiamate per la prima volta, anziché quando vengono caricate. Le funzioni di inizializzazione una sola volta assicurano che questa inizializzazione si verifichi una sola volta, anche quando più thread possono tentare l'inizializzazione.

Windows Server 2003 e Windows XP: Le applicazioni devono fornire la propria sincronizzazione per l'inizializzazione una sola volta usando le funzioni interlock o altri meccanismi di sincronizzazione. Le funzioni di inizializzazione una volta sono disponibili a partire da Windows Vista e Windows Server 2008.

Le funzioni di inizializzazione una volta offrono vantaggi significativi per garantire che solo un thread esegua l'inizializzazione:

  • Sono ottimizzati per la velocità.
  • Creano le barriere appropriate sulle architetture del processore che le richiedono.
  • Supportano sia l'inizializzazione bloccata che parallela.
  • Evitano il blocco interno in modo che il codice possa funzionare in modo asincrono o sincrono.

Il sistema gestisce il processo di inizializzazione tramite una struttura INIT_ONCE opaca che contiene dati e informazioni sullo stato. Il chiamante alloca questa struttura e lo inizializza chiamando InitOnceInitialize (per inizializzare la struttura in modo dinamico) o assegnando la costante INIT_ONCE_STATIC_INIT alla variabile di struttura (per inizializzare la struttura in modo statico). Inizialmente, i dati archiviati nella struttura di inizializzazione una volta sono NULL e il relativo stato non è inizializzato.

Le strutture di inizializzazione una volta non possono essere condivise tra processi.

Il thread che esegue l'inizializzazione può impostare facoltativamente un contesto disponibile per il chiamante dopo il completamento dell'inizializzazione. Il contesto può essere un oggetto di sincronizzazione oppure può essere un valore o una struttura di dati. Se il contesto è un valore, l'ordine basso INIT_ONCE_CTX_RESERVED_BITS deve essere zero. Se il contesto è una struttura di dati, la struttura dei dati deve essere allineata a DWORD. Il contesto viene restituito al chiamante nel parametro di output lpContext della funzione InitOnceBeginInitialize o InitOnceExecuteOnce .

L'inizializzazione una volta può essere eseguita in modo sincrono o asincrono. Una funzione di callback facoltativa può essere usata per l'inizializzazione una sola volta sincrona.

Inizializzazione monouso sincrona

I passaggi seguenti descrivono l'inizializzazione one-time sincrona che non usa una funzione di callback.

  1. Il primo thread da chiamare la funzione InitOnceBeginInitialize causa l'inizio dell'inizializzazione una sola volta. Per l'inizializzazione one-time sincrona, è necessario chiamare InitOnceBeginInitialize senza il flag INIT_ONCE_ASYNC .
  2. I thread successivi che tentano l'inizializzazione vengono bloccati finché il primo thread completa l'inizializzazione o non riesce. Se il primo thread ha esito negativo, il thread successivo può tentare l'inizializzazione e così via.
  3. Al termine dell'inizializzazione, il thread chiama la funzione InitOnceComplete . Il thread può facoltativamente creare un oggetto di sincronizzazione (o altri dati di contesto) e specificarlo nel parametro lpContext della funzione InitOnceComplete .
  4. Se l'inizializzazione ha esito positivo, lo stato della struttura di inizializzazione una volta viene modificato in inizializzato e l'handle lpContext (se presente) viene archiviato nella struttura di inizializzazione. I tentativi di inizializzazione successivi restituiscono questi dati di contesto. Se l'inizializzazione ha esito negativo, i dati sono NULL.

I passaggi seguenti descrivono l'inizializzazione one-time sincrona che usa una funzione di callback.

  1. Il primo thread per chiamare correttamente la funzione InitOnceExecuteOnce passa un puntatore a una funzione di callback definita dall'applicazione e a qualsiasi dato richiesto dalla funzione di callback. Se la chiamata ha esito positivo, viene eseguita la funzione callback InitOnceCallback .
  2. I thread successivi che tentano l'inizializzazione vengono bloccati finché il primo thread completa l'inizializzazione o non riesce. Se il primo thread ha esito negativo, il thread successivo può tentare l'inizializzazione e così via.
  3. Al termine dell'inizializzazione, la funzione di callback restituisce. La funzione di callback può facoltativamente creare un oggetto di sincronizzazione (o altri dati di contesto) e specificarlo nel parametro di output Context .
  4. Se l'inizializzazione ha esito positivo, lo stato della struttura di inizializzazione una volta viene modificato in inizializzato e l'handle Context (se presente) viene archiviato nella struttura di inizializzazione. I tentativi di inizializzazione successivi restituiscono questi dati di contesto. Se l'inizializzazione ha esito negativo, i dati sono NULL.

Inizializzazione one-time asincrona

I passaggi seguenti descrivono l'inizializzazione one-time asincrona.

  1. Se più thread tentano simultaneamente di iniziare l'inizializzazione chiamando InitOnceBeginInitialize con INIT_ONCE_ASYNC, la funzione ha esito positivo per tutti i thread con il parametro fPending impostato su TRUE. Solo un thread avrà esito positivo all'inizializzazione; altri tentativi simultanei non modificano lo stato di inizializzazione.
  2. Quando InitOnceBeginInitialize restituisce, il parametro fPending indica lo stato di inizializzazione:
    • Se fPending è FALSE, un thread ha avuto esito positivo all'inizializzazione. Altri thread devono pulire tutti i dati di contesto creati e usare i dati di contesto nel parametro di output lpContext di InitOnceBeginInitialize.
    • Se fPending è TRUE, l'inizializzazione non è ancora stata completata e altri thread devono continuare.
  3. Ogni thread chiama la funzione InitOnceComplete . Il thread può facoltativamente creare un oggetto di sincronizzazione (o altri dati di contesto) e specificarlo nel parametro lpContext di InitOnceComplete.
  4. Quando InitOnceComplete restituisce , il valore restituito indica se il thread chiamante ha avuto esito positivo all'inizializzazione.
    • Se InitOnceComplete ha esito positivo, il thread chiamante ha avuto esito positivo all'inizializzazione. Lo stato della struttura di inizializzazione una volta viene modificato in inizializzato e l'handle lpContext (se presente) viene archiviato nella struttura di inizializzazione.
    • Se InitOnceComplete ha esito negativo, un altro thread ha avuto esito positivo all'inizializzazione. Il thread chiamante deve pulire tutti i dati di contesto creati e chiamare InitOnceBeginInitialize con INIT_ONCE_CHECK_ONLY per recuperare tutti i dati di contesto archiviati nella struttura di inizializzazione one-time.

Chiamata di One-Time inizializzazione da più siti

L'inizializzazione one-time protetta da una singola struttura INIT_ONCE può essere eseguita da siti di mutiple; il callback diverso può essere passato da ogni sito e la sincronizzazione con e senza callback può essere mista. L'inizializzazione è ancora garantita per eseguire in modo sucesso una sola volta.

Tuttavia, l'inizializzazione asincrona e sincrona non può essere mista: una volta tentata l'inizializzazione asincrona, i tentativi di avviare l'inizializzazione sincrona non riuscirebbero.

Uso di One-Time inizializzazione