Einmalige Initialisierung

Komponenten sind oft so konzipiert, dass sie Initialisierungsaufgaben durchführen, wenn sie zum ersten Mal aufgerufen werden, und nicht, wenn sie geladen werden. Die einmaligen Initialisierungsfunktionen stellen sicher, dass diese Initialisierung nur einmal erfolgt, auch wenn mehrere Threads die Initialisierung versuchen können.

Windows Server 2003 und Windows XP: Anwendungen müssen ihre eigene Synchronisierung für einmalige Initialisierung mithilfe der verriegelten Funktionen oder eines anderen Synchronisierungsmechanismus bereitstellen. Die einmaligen Initialisierungsfunktionen sind ab Windows Vista und Windows Server 2008 verfügbar.

Die einmaligen Initialisierungsfunktionen bieten erhebliche Vorteile, um sicherzustellen, dass nur ein Thread die Initialisierung durchführt:

  • Sie sind für Geschwindigkeit optimiert.
  • Sie schaffen die entsprechenden Barrieren auf Prozessorarchitekturen, die diese benötigen.
  • Sie unterstützen sowohl gesperrte als auch parallele Initialisierung.
  • Sie vermeiden die interne Sperrung, sodass der Code asynchron oder synchron ausgeführt werden kann.

Das System verwaltet den Initialisierungsprozess über eine undurchsichtige INIT_ONCE Struktur, die Daten- und Zustandsinformationen enthält. Der Aufrufer weist diese Struktur zu und initialisiert sie entweder durch Aufrufen von InitOnceInitialize (zum dynamischen Initialisieren der Struktur) oder Zuweisen der Konstanten INIT_ONCE_STATIC_INIT zur Strukturvariablen (zum statischen Initialisieren der Struktur). Zunächst sind die in der einmaligen Initialisierungsstruktur gespeicherten Daten NULL, und ihr Zustand wird nicht initialisiert.

Einmalige Initialisierungsstrukturen können nicht über Prozesse hinweg gemeinsam genutzt werden.

Der Thread, der die Initialisierung durchführt, kann optional einen Kontext festlegen, der nach Abschluss der Initialisierung für den Aufrufer verfügbar ist. Der Kontext kann ein Synchronisierungsobjekt sein, oder es kann sich um einen Wert oder eine Datenstruktur handeln. Wenn der Kontext ein Wert ist, muss die niedrige Reihenfolge INIT_ONCE_CTX_RESERVED_BITS null sein. Wenn der Kontext eine Datenstruktur ist, muss die Datenstruktur DWORD-ausgerichtet sein. Der Kontext wird an den Aufrufer im lpContext-Ausgabeparameter der Funktion InitOnceBeginInitialize oder InitOnceExecuteOnce zurückgegeben.

Einmalige Initialisierung kann synchron oder asynchron durchgeführt werden. Eine optionale Rückruffunktion kann für die synchrone einmalige Initialisierung verwendet werden.

Synchrone einmalige Initialisierung

Die folgenden Schritte beschreiben die synchrone einmalige Initialisierung, die keine Rückruffunktion verwendet.

  1. Der erste Thread, der die InitOnceBeginInitialize-Funktion aufruft, führt zu einem erfolgreichen Start der einmaligen Initialisierung. Für die synchrone einmalige Initialisierung muss InitOnceBeginInitialize ohne das INIT_ONCE_ASYNC-Flag aufgerufen werden.
  2. Nachfolgende Threads, die die Initialisierung versuchen, werden blockiert, bis der erste Thread entweder die Initialisierung abgeschlossen oder fehlschlägt. Wenn der erste Thread fehlschlägt, kann der nächste Thread versuchen, die Initialisierung zu versuchen usw.
  3. Wenn die Initialisierung abgeschlossen ist, ruft der Thread die InitOnceComplete-Funktion auf. Der Thread kann optional ein Synchronisierungsobjekt (oder andere Kontextdaten) erstellen und sie im lpContext-Parameter der InitOnceComplete-Funktion angeben.
  4. Wenn die Initialisierung erfolgreich ist, wird der Status der einmaligen Initialisierungsstruktur in initialisiert geändert und das lpContext-Handle (falls vorhanden) in der Initialisierungsstruktur gespeichert. Nachfolgende Initialisierungsversuche geben diese Kontextdaten zurück. Wenn die Initialisierung fehlschlägt, sind die Daten NULL.

Die folgenden Schritte beschreiben die synchrone einmalige Initialisierung, die eine Rückruffunktion verwendet.

  1. Der erste Thread, der die InitOnceExecuteOnce-Funktion erfolgreich aufruft, übergibt einen Zeiger an eine anwendungsdefinierte InitOnceCallback-Rückruffunktion und alle Daten, die von der Rückruffunktion benötigt werden. Wenn der Aufruf erfolgreich ist, wird die InitOnceCallback-Rückruffunktion ausgeführt.
  2. Nachfolgende Threads, die die Initialisierung versuchen, werden blockiert, bis der erste Thread entweder die Initialisierung abgeschlossen oder fehlschlägt. Wenn der erste Thread fehlschlägt, kann der nächste Thread versuchen, die Initialisierung zu versuchen usw.
  3. Nach Abschluss der Initialisierung wird die Rückruffunktion zurückgegeben. Die Rückruffunktion kann optional ein Synchronisierungsobjekt (oder andere Kontextdaten) erstellen und im Kontextausgabeparameter angeben.
  4. Wenn die Initialisierung erfolgreich ist, wird der Status der einmaligen Initialisierungsstruktur in initialisiert geändert und das lpContext-Handle (falls vorhanden) in der Initialisierungsstruktur gespeichert. Nachfolgende Initialisierungsversuche geben diese Kontextdaten zurück. Wenn die Initialisierung fehlschlägt, sind die Daten NULL.

Asynchrone einmalige Initialisierung

Die folgenden Schritte beschreiben die asynchrone einmalige Initialisierung.

  1. Wenn mehrere Threads gleichzeitig versuchen, mit der Initialisierung zu beginnen, indem InitOnceBeginInitialize mit INIT_ONCE_ASYNC aufgerufen wird, wird die Funktion für alle Threads erfolgreich ausgeführt, wobei der Parameter fPending auf TRUE festgelegt ist. Bei der Initialisierung ist tatsächlich nur ein Thread erfolgreich; andere gleichzeitige Versuche ändern den Initialisierungszustand nicht.
  2. Wenn InitOnceBeginInitialize zurückgegeben wird, gibt der Parameter fPending den Initialisierungsstatus an:
    • Wenn fPending FALSE ist, wurde bei der Initialisierung ein Thread erfolgreich ausgeführt. Andere Threads sollten alle kontextbezogenen Daten bereinigen, die sie erstellt haben, und die Kontextdaten im lpContext-Ausgabeparameter von InitOnceBeginInitialize verwenden.
    • Wenn fPending TRUE ist, wurde die Initialisierung noch nicht abgeschlossen, und andere Threads sollten fortgesetzt werden.
  3. Jeder Thread ruft die InitOnceComplete-Funktion auf. Der Thread kann optional ein Synchronisierungsobjekt (oder andere Kontextdaten) erstellen und sie im lpContext-Parameter vom InitOnceComplete angeben.
  4. Wenn InitOnceComplete zurückgegeben wird, gibt der Rückgabewert an, ob der aufrufende Thread bei der Initialisierung erfolgreich war.
    • Wenn InitOnceComplete erfolgreich ist, wurde der aufrufende Thread bei der Initialisierung erfolgreich ausgeführt. Der Status der einmaligen Initialisierungsstruktur wird in initialisiert geändert und das lpContext-Handle (falls vorhanden) in der Initialisierungsstruktur gespeichert.
    • Wenn InitOnceComplete fehlschlägt, wurde bei der Initialisierung ein weiterer Thread erfolgreich ausgeführt. Der aufrufende Thread sollte alle kontextbezogenen Daten bereiigen, die er erstellt hat, und InitOnceBeginInitialize mit INIT_ONCE_CHECK_ONLY aufrufen, um alle Kontextdaten abzurufen, die in der einmaligen Initialisierungsstruktur gespeichert sind.

Aufrufen der einmaligen Initialisierung von mehreren Websites aus

Die einmalige Initialisierung, die durch eine einzelne INIT_ONCE-Struktur geschützt wird, kann von mehreren Standorten ausgeführt werden. Verschiedene Rückrufe können von jedem Standort übergeben werden, und die Synchronisierung mit und ohne Rückruf kann gemischt werden. Es wird weiterhin garantiert, dass die Initialisierung nur einmal erfolgreich durchgeführt wird.

Die asynchrone und synchrone Initialisierung kann jedoch nicht gemischt werden: Nach dem Versuch der asynchronen Initialisierung schlagen Versuche zum Starten der synchronen Initialisierung fehl.

Verwenden der einmaligen Initialisierung