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 initOnceBeginInitializeInitOnceExecuteOnce 函数的输出参数中的调用方。

一次性初始化可以同步或异步执行。 可选回调函数可用于同步一次性初始化。

同步一次性初始化

以下步骤描述不使用回调函数的同步一次性初始化。

  1. 调用 InitOnceBeginInitialize 函数的第一个线程成功导致一次性初始化开始。 对于同步一次性初始化,必须调用 InitOnceBeginInitialize 而不调用 INIT_ONCE_ASYNC 标志。
  2. 尝试初始化的后续线程将被阻止,直到第一个线程完成初始化或失败。 如果第一个线程失败,则允许下一个线程尝试初始化,依此等。
  3. 初始化完成后,线程将调用 InitOnceComplete 函数。 线程可以选择创建同步对象(或其他上下文数据),并在 InitOnceComplete 函数的 lpContext 参数中指定它。
  4. 如果初始化成功,则一次性初始化结构的状态将更改为初始化,lpContext 句柄(如果有)存储在初始化结构中。 后续初始化尝试返回此上下文数据。 如果初始化失败,则数据 NULL

以下步骤描述使用回调函数的同步一次性初始化。

  1. 成功调用 InitOnceExecuteOnce 函数的第一个线程将指针传递给应用程序定义的 InitOnceCallback 回调函数和回调函数所需的任何数据。 如果调用成功,则 InitOnceCallback 回调函数执行。
  2. 尝试初始化的后续线程将被阻止,直到第一个线程完成初始化或失败。 如果第一个线程失败,则允许下一个线程尝试初始化,依此等。
  3. 初始化完成后,回调函数将返回。 回调函数可以选择创建同步对象(或其他上下文数据),并在其 上下文 输出参数中指定它。
  4. 如果初始化成功,则一次性初始化结构的状态将更改为初始化,上下文 句柄(如果有)存储在初始化结构中。 后续初始化尝试返回此上下文数据。 如果初始化失败,则数据 NULL

异步一次性初始化

以下步骤描述异步一次性初始化。

  1. 如果多个线程同时尝试通过调用 InitOnceBeginInitializeINIT_ONCE_ASYNC来开始初始化,则所有线程的函数都成功,fPending 参数设置为 TRUE。 在初始化时,实际上只有一个线程会成功;其他并发尝试不会更改初始化状态。
  2. InitOnceBeginInitialize 返回时,fPending 参数指示初始化状态:
    • 如果 fPendingFALSE,则初始化时已成功执行一个线程。 其他线程应清理他们创建的任何上下文数据,并在 lpContextInitOnceBeginInitialize的输出参数中使用上下文数据。
    • 如果 fPendingTRUE,则初始化尚未完成,其他线程应继续。
  3. 每个线程调用 InitOnceComplete 函数。 线程可以选择创建同步对象(或其他上下文数据),并在 InitOnceCompletelpContext 参数中指定它。
  4. InitOnceComplete 返回时,其返回值指示调用线程在初始化时是否成功。
    • 如果 InitOnceComplete 成功,则调用线程在初始化时已成功。 一次性初始化结构的状态更改为初始化,lpContext 句柄(如果有)存储在初始化结构中。
    • 如果 InitOnceComplete 失败,则另一个线程在初始化时已成功。 调用线程应清理已创建的任何上下文数据,并使用 INIT_ONCE_CHECK_ONLY 调用 InitOnceBeginInitialize,以检索存储在一次性初始化结构中的任何上下文数据。

从多个站点调用 One-Time 初始化

一次性初始化由单个 INIT_ONCE 结构保护,可以从多个站点执行;可以从每个站点传递不同的回调,并且使用和不使用回调的同步可能会混合。 初始化仍保证仅成功执行一次。

但是,异步和同步初始化不能混合:尝试异步初始化后,尝试启动同步初始化会失败。

使用 One-Time 初始化