組件通常設計為在首次調用時執行初始化任務,而不是加載它們時。 一次性初始化函數可確保此初始化僅發生一次,即使多個線程可能嘗試初始化也是如此。
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 函數的輸出參數中的調用方。
一次性初始化可以同步或異步執行。 可選回調函數可用于同步一次性初始化。
同步一次性初始化
以下步驟描述不使用回調函數的同步一次性初始化。
- 調用 InitOnceBeginInitialize 函數的第一個線程成功導致一次性初始化開始。 對于同步一次性初始化,必須調用 InitOnceBeginInitialize 而不調用 INIT_ONCE_ASYNC 標志。
- 嘗試初始化的后續線程將被阻止,直到第一個線程完成初始化或失敗。 如果第一個線程失敗,則允許下一個線程嘗試初始化,依此等。
- 初始化完成后,線程將調用 InitOnceComplete 函數。 線程可以選擇創建同步對象(或其他上下文數據),并在 InitOnceComplete 函數的 lpContext 參數中指定它。
- 如果初始化成功,則一次性初始化結構的狀態將更改為初始化,lpContext 句柄(如果有)存儲在初始化結構中。 后續初始化嘗試返回此上下文數據。 如果初始化失敗,則數據 NULL。
以下步驟描述使用回調函數的同步一次性初始化。
- 成功調用 InitOnceExecuteOnce 函數的第一個線程將指針傳遞給應用程序定義的 InitOnceCallback 回調函數和回調函數所需的任何數據。 如果調用成功,則 InitOnceCallback 回調函數執行。
- 嘗試初始化的后續線程將被阻止,直到第一個線程完成初始化或失敗。 如果第一個線程失敗,則允許下一個線程嘗試初始化,依此等。
- 初始化完成后,回調函數將返回。 回調函數可以選擇創建同步對象(或其他上下文數據),并在其 上下文 輸出參數中指定它。
- 如果初始化成功,則一次性初始化結構的狀態將更改為初始化,上下文 句柄(如果有)存儲在初始化結構中。 后續初始化嘗試返回此上下文數據。 如果初始化失敗,則數據 NULL。
異步一次性初始化
以下步驟描述異步一次性初始化。
1. 如果多個線程同時嘗試通過調用 InitOnceBeginInitialize 和 INIT_ONCE_ASYNC來開始初始化,則所有線程的函數都成功,fPending 參數設置為 TRUE。 在初始化時,實際上只有一個線程會成功;其他并發嘗試不會更改初始化狀態。
2. InitOnceBeginInitialize 返回時,fPending 參數指示初始化狀態:
- 如果 fPendingFALSE,則初始化時已成功執行一個線程。 其他線程應清理他們創建的任何上下文數據,并在 lpContextInitOnceBeginInitialize的輸出參數中使用上下文數據。
- 如果 fPendingTRUE,則初始化尚未完成,其他線程應繼續。
3. 每個線程調用 InitOnceComplete 函數。 線程可以選擇創建同步對象(或其他上下文數據),并在 InitOnceComplete的 lpContext 參數中指定它。
4. InitOnceComplete 返回時,其返回值指示調用線程在初始化時是否成功。
- 如果 InitOnceComplete 成功,則調用線程在初始化時已成功。 一次性初始化結構的狀態更改為初始化,lpContext 句柄(如果有)存儲在初始化結構中。
- 如果 InitOnceComplete 失敗,則另一個線程在初始化時已成功。 調用線程應清理已創建的任何上下文數據,并使用 INIT_ONCE_CHECK_ONLY 調用 InitOnceBeginInitialize,以檢索存儲在一次性初始化結構中的任何上下文數據。
從多個站點調用 一次性 初始化
一次性初始化由單個 INIT_ONCE 結構保護,可以從多個站點執行;可以從每個站點傳遞不同的回調,并且使用和不使用回調的同步可能會混合。 初始化仍保證僅成功執行一次。
但是,異步和同步初始化不能混合:嘗試異步初始化后,嘗試啟動同步初始化會失敗。