Поделиться через


Инициализация One-Time

Компоненты часто предназначены для выполнения задач инициализации при первом вызове, а не при загрузке. Функции одноразовой инициализации гарантируют, что эта инициализация выполняется только один раз, даже если несколько потоков могут попытаться инициализацию.

Windows Server 2003 и Windows XP: Приложения должны обеспечить собственную синхронизацию для одноразовой инициализации с помощью взаимосвязанных функций или другого механизма синхронизации. Функции одноразовой инициализации доступны начиная с Windows Vista и Windows Server 2008.

Функции одноразовой инициализации предоставляют значительные преимущества, гарантирующие, что инициализацию выполняет только один поток:

  • Они оптимизированы для скорости.
  • Они создают соответствующие барьеры для архитектур процессоров, которым они требуются.
  • Они поддерживают как заблокированную, так и параллельную инициализацию.
  • Они избегают внутренней блокировки, поэтому код может работать асинхронно или синхронно.

Система управляет процессом инициализации с помощью непрозрачной структуры INIT_ONCE , содержащей данные и сведения о состоянии. Вызывающий объект выделяет эту структуру и инициализирует ее путем вызова InitOnceInitialize (для динамической инициализации структуры) или назначения константы INIT_ONCE_STATIC_INIT переменной структуры (для статической инициализации структуры). Изначально данные, хранящиеся в структуре одноразовой инициализации, будут иметь значение NULL, а их состояние не инициализировано.

Структуры одноразовой инициализации нельзя совместно использовать в разных процессах.

Поток, выполняющий инициализацию, может при необходимости задать контекст, доступный вызывающей объекту после завершения инициализации. Контекст может быть объектом синхронизации или значением или структурой данных. Если контекст является значением, его INIT_ONCE_CTX_RESERVED_BITS низкого порядка должен быть равен нулю. Если контекст является структурой данных, структура данных должна быть выровнена по DWORD. Контекст возвращается вызывающей объекту в выходном параметре lpContext функции InitOnceBeginInitialize или InitOnceExecuteOnce .

Однократная инициализация может выполняться синхронно или асинхронно. Для синхронной одноразовой инициализации можно использовать необязательную функцию обратного вызова.

Синхронная одноразовая инициализация

Ниже описана синхронная одноразовая инициализация, которая не использует функцию обратного вызова.

  1. Первый поток, вызывающий функцию InitOnceBeginInitialize , успешно приводит к началу однократной инициализации. Для синхронной одноразовой инициализации метод InitOnceBeginInitialize должен вызываться без флага INIT_ONCE_ASYNC .
  2. Последующие потоки, которые пытаются инициалировать, блокируются до тех пор, пока первый поток не завершит инициализацию или не завершится сбоем. Если первый поток завершается сбоем, следующему потоку разрешается попытка инициализации и т. д.
  3. После завершения инициализации поток вызывает функцию InitOnceComplete . При необходимости поток может создать объект синхронизации (или другие данные контекста) и указать его в параметре lpContext функции InitOnceComplete .
  4. Если инициализация выполнена успешно, состояние структуры одноразовой инициализации изменяется на инициализировано, а дескриптор lpContext (при наличии) сохраняется в структуре инициализации. Последующие попытки инициализации возвращают эти данные контекста. Если инициализация завершается сбоем, данные будут иметь значение NULL.

Ниже описана синхронная одноразовая инициализация, использующая функцию обратного вызова.

  1. Первый поток, успешно вызывающий функцию InitOnceExecuteOnce, передает указатель на определяемую приложением функцию обратного вызова InitOnceCallback и все данные, необходимые для функции обратного вызова. Если вызов выполнен успешно, выполняется функция обратного вызова InitOnceCallback .
  2. Последующие потоки, которые пытаются инициалировать, блокируются до тех пор, пока первый поток не завершит инициализацию или не завершится сбоем. Если первый поток завершается сбоем, следующему потоку разрешается попытка инициализации и т. д.
  3. После завершения инициализации функция обратного вызова возвращает . Функция обратного вызова может при необходимости создать объект синхронизации (или другие данные контекста) и указать его в выходном параметре Context .
  4. Если инициализация выполнена успешно, состояние структуры одноразовой инициализации изменяется на инициализировано, а дескриптор контекста (если таковой имеется) сохраняется в структуре инициализации. Последующие попытки инициализации возвращают эти данные контекста. Если инициализация завершается сбоем, данные будут иметь значение NULL.

Асинхронная одноразовая инициализация

Ниже описана асинхронная одноразовая инициализация.

  1. Если несколько потоков одновременно пытаются начать инициализацию путем вызова InitOnceBeginInitialize с INIT_ONCE_ASYNC, функция выполняется успешно для всех потоков с параметром fPending, для параметра fPending задано значение TRUE. На самом деле при инициализации будет успешно выполнен только один поток; другие параллельные попытки не изменяют состояние инициализации.
  2. При возвращении InitOnceBeginInitialize параметр fPending указывает состояние инициализации:
    • Если fPending имеет значение FALSE, один поток успешно выполнен при инициализации. Другие потоки должны очистить все созданные ими контекстные данные и использовать их в выходном параметре lpContextinitOnceBeginInitialize.
    • Если fPending имеет значение TRUE, инициализация еще не завершена и другие потоки должны продолжаться.
  3. Каждый поток вызывает функцию InitOnceComplete . При необходимости поток может создать объект синхронизации (или другие данные контекста) и указать его в параметре lpContextinitOnceComplete.
  4. При возвращении InitOnceComplete его возвращаемое значение указывает, успешно ли был выполнен вызывающий поток при инициализации.
    • Если InitOnceComplete завершается успешно, вызывающий поток успешно выполнен при инициализации. Состояние структуры одноразовой инициализации изменяется на инициализировано, а дескриптор lpContext (если таковой имеется) сохраняется в структуре инициализации.
    • Если initOnceComplete завершается сбоем , при инициализации успешно выполняется другой поток. Вызывающий поток должен очистить все созданные им контекстные данные и вызвать InitOnceBeginInitialize с INIT_ONCE_CHECK_ONLY , чтобы получить все данные контекста, хранящиеся в структуре одноразовой инициализации.

Вызов инициализации One-Time с нескольких сайтов

Одноразовая инициализация, защищенная одной структурой INIT_ONCE , может выполняться с мутип-сайтов; С каждого сайта можно передавать разные обратные вызовы, а синхронизация с обратным вызовом и без нее может быть смешанной. Инициализация по-прежнему гарантирует выполнение однократно.

Однако асинхронную и синхронную инициализацию нельзя смешивать: после попытки асинхронной инициализации попытки начать синхронную инициализацию завершатся ошибкой.

Использование инициализации One-Time