Udostępnij za pośrednictwem


inicjowanie One-Time

Składniki są często przeznaczone do wykonywania zadań inicjowania, gdy są one wywoływane po raz pierwszy, a nie podczas ich ładowania. Funkcje inicjowania jednorazowego zapewniają, że inicjowanie odbywa się tylko raz, nawet jeśli wiele wątków może podjąć próbę zainicjowania.

Windows Server 2003 i Windows XP: aplikacje muszą zapewnić własną synchronizację na potrzeby jednorazowej inicjowania przy użyciu funkcji połączonych lub innego mechanizmu synchronizacji. Funkcje inicjowania jednorazowego są dostępne począwszy od systemów Windows Vista i Windows Server 2008.

Funkcje inicjowania jednorazowego zapewniają znaczne korzyści, aby zapewnić, że tylko jeden wątek wykonuje inicjowanie:

  • Są one zoptymalizowane pod kątem szybkości.
  • Tworzą one odpowiednie bariery dla architektur procesorów, które ich wymagają.
  • Obsługują zarówno zablokowane, jak i równoległe inicjowanie.
  • Unikają wewnętrznego blokowania, dzięki czemu kod może działać asynchronicznie lub synchronicznie.

System zarządza procesem inicjowania za pomocą nieprzezroczystej struktury INIT_ONCE zawierającej informacje o danych i stanie. Obiekt wywołujący przydziela tę strukturę i inicjuje ją przez wywołanie metody InitOnceInitialize (w celu dynamicznego zainicjowania struktury) lub przypisania stałej INIT_ONCE_STATIC_INIT do zmiennej struktury (w celu zainicjowania struktury statycznie). Początkowo dane przechowywane w strukturze inicjowania jednorazowego mają wartość NULL, a ich stan jest niezainicjowany.

Nie można współdzielić struktur inicjowania jednorazowego między procesami.

Wątek wykonujący inicjowanie może opcjonalnie ustawić kontekst dostępny dla obiektu wywołującego po zakończeniu inicjowania. Kontekst może być obiektem synchronizacji lub może być wartością lub strukturą danych. Jeśli kontekst jest wartością, jego INIT_ONCE_CTX_RESERVED_BITS o niskiej kolejności musi być równa zero. Jeśli kontekst jest strukturą danych, struktura danych musi być DWORD-aligned. Kontekst jest zwracany do obiektu wywołującego w lpContext parametr wyjściowy InitOnceBeginInitialize lub initOnceExecuteOnceOnce funkcji.

Inicjowanie jednorazowe może być wykonywane synchronicznie lub asynchronicznie. Opcjonalna funkcja wywołania zwrotnego może służyć do synchronicznej jednorazowej inicjowania.

Synchroniczne jednorazowe inicjowanie

W poniższych krokach opisano synchroniczne jednorazowe inicjowanie, które nie używa funkcji wywołania zwrotnego.

  1. Pierwszy wątek wywołujący funkcję InitOnceBeginIniginInitialize pomyślnie powoduje rozpoczęcie inicjowania jednorazowego. W przypadku jednorazowej inicjalizacji synchronicznej należy wywołać InitOnceBeginInitialize bez flagi INIT_ONCE_ASYNC.
  2. Kolejne wątki, które próbują zainicjować, są blokowane do momentu ukończenia inicjowania pierwszego wątku lub niepowodzenia. Jeśli pierwszy wątek zakończy się niepowodzeniem, następny wątek może podjąć próbę zainicjowania itd.
  3. Po zakończeniu inicjowania wątek wywołuje funkcję InitOnceComplete. Wątek może opcjonalnie utworzyć obiekt synchronizacji (lub inne dane kontekstowe) i określić go w lpContext parametru funkcji InitOnceComplete.
  4. Jeśli inicjowanie zakończy się pomyślnie, stan jednorazowej struktury inicjowania zostanie zmieniony na zainicjowany, a uchwyt lpContext (jeśli istnieje) jest przechowywany w strukturze inicjowania. Kolejne próby inicjowania zwracają te dane kontekstu. Jeśli inicjowanie zakończy się niepowodzeniem, dane są wartości null.

W poniższych krokach opisano synchroniczne jednorazowe inicjowanie, które używa funkcji wywołania zwrotnego.

  1. Pierwszy wątek do pomyślnego wywołania funkcji InitOnceExecuteOnce przekazuje wskaźnik do zdefiniowanej przez aplikację initOnceCallback funkcji wywołania zwrotnego i wszelkich danych wymaganych przez funkcję wywołania zwrotnego. Jeśli wywołanie powiedzie się, zostanie wykonana funkcja InitOnceCallback wywołania zwrotnego.
  2. Kolejne wątki, które próbują zainicjować, są blokowane do momentu ukończenia inicjowania pierwszego wątku lub niepowodzenia. Jeśli pierwszy wątek zakończy się niepowodzeniem, następny wątek może podjąć próbę zainicjowania itd.
  3. Po zakończeniu inicjowania funkcja wywołania zwrotnego zwraca wartość . Funkcja wywołania zwrotnego może opcjonalnie utworzyć obiekt synchronizacji (lub inne dane kontekstowe) i określić go w parametrze wyjściowym Context.
  4. Jeśli inicjowanie zakończy się pomyślnie, stan jednorazowej struktury inicjowania zostanie zmieniony na zainicjowany, a kontekst dojście (jeśli istnieje) jest przechowywane w strukturze inicjowania. Kolejne próby inicjowania zwracają te dane kontekstu. Jeśli inicjowanie zakończy się niepowodzeniem, dane są wartości null.

Asynchroniczne jednorazowe inicjowanie

W poniższych krokach opisano asynchroniczne jednorazowe inicjowanie.

  1. Jeśli wiele wątków jednocześnie próbuje rozpocząć inicjowanie, wywołując InitOnceBeginInitialize za pomocą INIT_ONCE_ASYNC, funkcja powiedzie się dla wszystkich wątków z parametrem fPending ustawionym na true. Tylko jeden wątek rzeczywiście zakończy się powodzeniem podczas inicjowania; inne próby współbieżne nie zmieniają stanu inicjowania.
  2. Gdy funkcja InitOnceBeginInitialize zostanie zwrócona, parametr fPending wskazuje stan inicjowania:
    • Jeśli fPending jest false, jeden wątek zakończył się pomyślnie podczas inicjowania. Inne wątki powinny wyczyścić wszystkie utworzone dane kontekstu i używać danych kontekstowych w lpContext parametru wyjściowego InitOnceBeginInitialize.
    • Jeśli fPending jest true, inicjowanie nie zostało jeszcze ukończone, a inne wątki powinny być kontynuowane.
  3. Każdy wątek wywołuje funkcję InitOnceComplete. Wątek może opcjonalnie utworzyć obiekt synchronizacji (lub inne dane kontekstu) i określić go w lpContext parametru InitOnceComplete.
  4. Gdy funkcja InitOnceComplete zwraca, zwracana wartość wskazuje, czy wywołanie wątku zakończyło się pomyślnie podczas inicjowania.
    • Jeśli initOnceComplete powiedzie się, wątek wywołujący zakończył się pomyślnie podczas inicjowania. Stan jednorazowej struktury inicjowania jest zmieniany na zainicjowany, a dojście lpContext (jeśli istnieje) jest przechowywane w strukturze inicjowania.
    • Jeśli initOnceComplete zakończy się niepowodzeniem, inny wątek zakończył się pomyślnie podczas inicjowania. Wątek wywołujący powinien wyczyścić wszystkie utworzone dane kontekstu i wywołać InitOnceBeginInitialize za pomocą INIT_ONCE_CHECK_ONLY, aby pobrać wszystkie dane kontekstowe przechowywane w strukturze inicjowania jednorazowego.

Wywoływanie inicjowania One-Time z wielu lokacji

Jednorazowa inicjalizacja chroniona przez jedną strukturę INIT_ONCE może być wykonywana z wielu lokacji; różne wywołania zwrotne mogą być przekazywane z każdej lokacji, a synchronizacja z wywołaniem zwrotnym i bez wywołania zwrotnego może być mieszana. Inicjowanie jest nadal gwarantowane, aby pomyślnie wykonać tylko raz.

Nie można jednak mieszać asynchronicznej i synchronicznej inicjalizacji: po podjęciu próby zainicjowania asynchronicznego próba rozpoczęcia synchronicznej inicjowania zakończy się niepowodzeniem.

używanie inicjowania One-Time